1
0
Fork 0
mirror of https://github.com/Oreolek/undum.git synced 2024-06-16 23:11:04 +03:00

Added license and readme.

This commit is contained in:
Ian Millington 2010-06-16 02:21:35 +01:00
parent e38ea7b9cc
commit 823742e559
5 changed files with 586 additions and 106 deletions

22
LICENSE Normal file
View file

@ -0,0 +1,22 @@
MIT License
----
Copyright (c) 2009-2010 Ian Millington
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

140
README.md Normal file
View file

@ -0,0 +1,140 @@
# Undum
Undum is a game framework for building a sophisticated form of
hypertext interactive fiction.
If that means nothing to you, then let's go back a few steps. Remember
those Choose Your Own Adventure, or Fighting Fantasy books? Where you
got to choose what your character does next? Well if you think of that
in a web-page you have hypertext interactive fiction, or HIF. Instead
of turning to a particular page, you click a link, and the next bit of
content appears.
The problem is that those kinds of games are pretty limited. Every
time the player does something, the story could go in different
directions. So the author has to either write masses of branches, or
else the decisions you make as a player have to be relatively short
lived. If you played CYOA books you'll know that the wrong move either
ended the story pretty quickly, or else it didn't really matter what
you did because you'd end up at the same place.
To beat this limitation, Undum allows you to make the output
dynamic. It allows you to keep track of what has happened to the
character (any kinds of data, in fact), and to then change the text
that gets output accordingly. Effectively it is like writing a CYOA
page that is different each time you read it. This allows for far
richer and more rewarding game design.
Undum is a pure client client-side library. It consists of a HTML file
and three Javascript files. The HTML file uses a nice bit of styling,
so there's a bunch of CSS and images in the default package too, but
that can be replaced if you want. To create your own game, you edit
the HTML file a little (mainly just changing the title and author),
and edit one of the Javascript files.
Because the game is written in Javascript, you get the full power of a
dynamic and efficient programming language. This isn't a CYOA
scripting system with limited functionality. You can take control of
anything you want. Or you can just keep things simple using a bunch of
simple functions provided by Undum.
## Compatibility
Undum is designed for HTML5 and CSS3 browsers. It has been tested on
Firefox 3.6, Chrome 5, and Safari 5. Older browsers may work okay too,
but some of the animation won't work, the styles may render poorly,
and saving and loading of games is unlikely to work. Anyone who wants
to hack around with it and make it work more widely is welcome. Just
fork this project on Github.
The local storage system on some browsers does not work when loading a
page from your hard drive. To test your game when developing it, you
may want to start up a simple local webserver. I have found that
Chrome seems to reliably provide local storage for local
development. It also has excellent Javascript debugging tools.
## Getting Started
1. Download Undum. Use the 'download source' link in the top right of
this page.
2. Unzip Undum somewhere on your hard-drive.
3. Open tutorial.html in your browser, and play through the tutorial
(see Progress, below).
4. Copy tutorial.html to a file that reflects your game name.
5. Edit your HTML file and add the title, author and description of
the game you want to write. At the bottom of the file change the
name of `tutorial.game.js` to something else (by convention
*your-game-name*`.game.js`.
6. Copy `tutorial.game.js` to the file name you chose in the last
step. Open it and begin creating your game.
The source code for all the files is heavily commented, so if you get
stuck, go in and read it.
## Deploying
To deploy your game, just upload the `index.html` and the `media`
folder to your webserver. You can serve several games with the same
look and feel from the same directory. Just rename `index.html` to a
different name for each game, and have each HTML file load the correct
`.game.js` file at the end. The remaining files will be reused.
For example, if you had 3 games: `episode1`, `episode2`, and
`christmas-special`. You'd have a directory structure:
episode1.html
episode2.html
christmas-special.html
media/
css/ ...
img/ ...
js/
jquery-1.4.2.min.js
undum.js
episode1.game.js
episode2.game.js
christmas-special.game.js
## Progress
My plan (if I get some time) is to make the very, very terse example
game into something that acts as a real tutorial as well as a
smoke-test. For now, I apologise that the "tutorial" is nothing of the
kind, just a shell used to test all major functions of the
code. Still, I think if you look at the code you'll see how just about
everything works. If you're interested in helping me put together
something on that front, let me know.
Also on my plan is some reference documentation. So you don't have to
go through the source code (no matter how well documented it is).
## Undum
The name `undum` came from a little project that preceded this code
base. In 1998 I put together a simple browser based game. It was
narrative, but used the grind-based mechanics of games such as
Farmville and Mafia Wars. Because of the grinding, I called it
Carborundum, which I found I couldn't type at speed, so it became
Undum. The code has changed out of all recognition since them, as the
grind-based game moved to Flash. But the name stuck for the Javascript
framework.
## License
The code is distributed under the MIT license. This permits you to
modify and use it, even for commercial use. The only stipulation is
that you keep the original copyright message associated with the
code. This appears as the bottom line in the HTML file. A copy of the
MIT license is found in the LICENSE file.

View file

@ -14,40 +14,82 @@ var FUDGE_WORDS = [
/* The situations that the game can be in. Each has a unique ID. */
undum.game.situations = {
start: new undum.ActionSituation(
"<p>It was a\
dark and stormy night. <span class='transient'>And nothing was\
stirring, not even a <a href='leave'>mouse</a>.</span> You could\
<a class='sticky' href='start/light'>light a candle</a>, to see \
what's <a href='/' class='raw'>going on</a>. Or you could\
<a href='leave'>leave</a>.</p>",
"<p>Welcome to the UNDUM tutorial. This text was generated by\
the <em>enter</em> method of the first situation. All interaction \
takes place in a situation. You can think of it either as a 'Room' in\
regular IF (although it is less flexible than that), or as a 'Page'\
in a Choose Your Own Adventure book (though it is more flexible than\
that.<p>\
\
<p>Two things happen in a situation. You either\
<a href='leave'>leave</a> the situation for another one, or else\
you carry out some <a href='./act'>action</a>. Actions perform\
some processing, they may display some results, but\
ultimately they put you back into the same situation\
again.</p>\
\
<p class='transient'>This paragraph gives you some\
options to click. Because the paragraph HTML tag has the\
<em>transient</em> CSS class, they are removed when you move\
out of this situation, so\
that the text that remains is more pleasing to read. To stop \
the player using links from old situations, you'll also notice \
that the links in the previous paragraph turn into plain text \
when you <a href='leave'>go</a> (you can stop this behavior by \
giving a link the <em>sticky</em> CSS class - particularly useful \
for offsite links). So what now? You could\
<a href='./act'>carry out an action</a>, use a link to go\
<a href='http://news.bbc.co.uk/' class='raw'>off site</a>\
(offsite links could be styled differently, but currently aren't).\
Or you could <a href='leave'>go to the next situation</a>.\
</p>",
// Responses to action codes:
{
light: function(character, system, action) {
system.animateQuality(
"luck", character.qualities.luck + 1, {
from: 0.6,
to: 0.2
}
act: function(character, system, action) {
system.scrollHere();
system.write(
"<p>You have carried out an action. This doesn't change\
the situation, but may modify the character in some\
way, and may generate some output. In this case\
I am increasing your 'luck' score, and you should see\
both an animation below, and the score changing in the\
qualities list to the right.</p>"
);
system.setQuality('blessed', 0);
return "<p>The lamp flickers into life.</p>";
system.animateQuality("luck", character.qualities.luck + 1);
system.write(
"<p>I am also removing the 'novice' quality (you can\
see it go from the character panel in the top right.</p>"
);
system.setQuality('novice', 0);
// We don't have to return content here, we could just do
// another write and return null. Either way has the same
// effect.
return "<p>And that's us done for this action. Because we end\
up in the same situation, you can click the action to \
repeat it. If you want to limit the user so they can't \
do the same thing again, it is best to send the user to \
another situation.</p>";
}
},
// Other options and function overrides:
{
heading: "Where You Start",
heading: "Starting Out with Undum",
exit: function(character, system, from) {
system.setQuality("magic", 1);
system.setCharacterText("You feel all magical!");
system.setQuality("inspiration", 1);
system.setCharacterText(
"<p>You feel all inspired, why not have a go?</p>"
);
return true;
},
}
),
leave: new undum.SimpleSituation(
"<p>You have left, goodbye.</p>"
"<p>This is the end of this tutorial. Have a dig through the code, \
particularly <em>undum.js</em>, for more details.</p>"
)
};
@ -70,11 +112,11 @@ undum.game.qualities = {
"Luck", FUDGE_WORDS, {offset:-4, priority:"0003", group:'stats'}
),
magic: new undum.IntegerQuality(
"Magic", {priority:"0001", group:'magic'}
inspiration: new undum.IntegerQuality(
"Inspiration", {priority:"0001", group:'progress'}
),
blessed: new undum.OnOffQuality(
"Blessed", {priority:"0002", group:'magic', onValue:"&#10003;"}
novice: new undum.OnOffQuality(
"Novice", {priority:"0002", group:'progress', onValue:"&#10003;"}
)
};
@ -85,8 +127,8 @@ undum.game.qualities = {
* the end. It is an error to have a quality definition belong to a
* non-existent group. */
undum.game.qualityGroups = {
stats: new undum.QualityGroup(null, {priority:"0000"}),
magic: new undum.QualityGroup('Magic', {priority:"0001"})
stats: new undum.QualityGroup(null, {priority:"0001"}),
progress: new undum.QualityGroup('Progress', {priority:"0002"})
};
// ---------------------------------------------------------------------------
@ -96,6 +138,6 @@ undum.game.init = function(character, system) {
character.qualities.skill = 12;
character.qualities.stamina = 12;
character.qualities.luck = 0;
character.qualities.blessed = 1;
system.setCharacterText("Hello.");
character.qualities.novice = 1;
system.setCharacterText("You are starting on an exciting journey.");
};

View file

@ -6,7 +6,9 @@
(function() {
// -----------------------------------------------------------------------
// Infrastructure Implementations
// Internal Infrastructure Implementations [NB: These have to be
// at the top, because we use them below, but you can safely
// ignore them and skip down to the next section.]
// -----------------------------------------------------------------------
/* Crockford's inherit function */
@ -40,10 +42,42 @@
}
// -----------------------------------------------------------------------
// Types for user instantiation.
// Types for Author Use
// -----------------------------------------------------------------------
/* The game is split into situations. */
/* The game is split into situations, which respond to user
* choices. Situation is the base type. It has three methods:
* enter, act and exit, which you implement to perform any
* processing and output any content. The default implementations
* do nothing.
*
* You can either create your own type of Situation, and add
* enter, act and/or exit functions to the prototype (see
* SimpleSituation or ActionSituation in this file for examples of
* that), or you can give those functions in the opts
* parameter. The opts parameter is an object. So you could write:
*
* var situation = Situation({
* enter: function(character, system, from) {
* ... your implementation ...
* }
* });
*
* If you pass in enter, act and/or exit through these options,
* then they should have the same function signature as the full
* function definitions, below.
*
* Note that the derived types of Situation (SimpleSituation and
* ActionSituation), call passed in functions AS WELL AS their
* normal action. This is most often what you want: the normal
* behavior plus a little extra custom behavior. If you want to
* override the behavior of a SimpleSituation or ActionSituation,
* you'll have to create a derived type and set the enter, act
* and/or exit function on their prototypes. In most cases,
* however, if you want to do something completely different, it
* is better to derive your type from this type: Situation, rather
* than one of its children.
*/
var Situation = function(opts) {
if (opts) {
if (opts.enter) this._enter = opts.enter;
@ -74,8 +108,15 @@
else return true;
};
/* A simple situation just displays its text when the situation is
* entered. The heading is optional. */
/* A simple situation has a block of content that it displays when
* the situation is entered. The content must be valid "Display
* Content" (see `System.prototype.write` for a definition). It
* has an optional `heading` (in the opts parameter) that will be
* used as a section title before the content is displayed. The
* heading can be any HTML string, it doesn't need to be "Display
* Content". The remaining options in the opts parameter are the
* same as for Situation.
*/
var SimpleSituation = function(content, opts) {
Situation.call(this, opts);
this.content = content;
@ -89,11 +130,18 @@
};
/* An action situation is just like a simple situation, only it
* has a number of fixed responses to internal actions. The
* actions parameter should be an object mapping the action id to
* the text response. Responses can be either a
* function(character, system, action) that returns a string of
* content, or just the raw string of content. */
* has a number of responses to internal actions that can occur
* without leaving the situation. The actions parameter should be
* an object mapping the action id to a response. Responses can be
* either a function(character, system, action) that returns a
* string of content, or just the raw string of content. In either
* case the content must be valid "Display Content" (see
* `System.prototype.write` for a definition). If you give a
* function, then the function may return null if it has no output
* to send (e.g. it could use system.prototype.write internally to
* write its output). Valid options in the opts parameter are the
* same as for SimpleSituation.
*/
var ActionSituation = function(content, actions, opts) {
SimpleSituation.call(this, content, opts);
this.actions = actions;
@ -105,21 +153,42 @@
response = response(character, system, action);
} catch (err) {
}
system.write(response);
if (response) system.write(response);
if (this._act) this._act(character, system, action);
};
/* Instances of this class define the qualities that characters
* may possess. The title should be a string, and can contain
* HTML. The priority, if given, is a string used to sort
* qualities within their groups. If you don't give a priority,
* then the title will be used. Normally you either don't give a
* priority, or else use a priority string containing 0-padded
* numbers (e.g. "00001"), because numbers sort before
* letters. The group parameter allows you to specify that
* this definition sits within a particular group. All qualities
* without a group are placed at the end of the list. */
* HTML. Options are passed in in the opts parameter. The
* following options are available.
*
* priority - A string used to sort qualities within their
* groups. When the system displays a list of qualities they
* will be sorted by this string. If you don't give a
* priority, then the title will be used, so you'll get
* alphabetic order. Normally you either don't give a
* priority, or else use a priority string containing 0-padded
* numbers (e.g. "00001").
*
* group - The Id of a group in which to display this
* parameter. If a group is given, then it must be defined in
* your `undum.game.qualityGroups` property. If no group is
* given, then the quality will be placed at the end of the
* list.
*
* extraClasses - These classes will be attached to the <div> tag
* that surrounds the quality when it is displayed. A common
* use for this is to add icons representing the quality. In
* your CSS define a class for each icon, then pass those
* classes into the appropriate quality definitions.
*
* One key purpose of QualityDefinition is to format the quality
* value for display. Quality values are always stored as numeric
* values, but may be displayed in words or symbols. A number of
* sub-types of QualityDefinition are given that format their
* values in different ways.
*/
var QualityDefinition = function(title, opts) {
var myOpts = $.extend({
priority: title,
@ -140,8 +209,9 @@
return value.toString();
};
/* A Quality that is always displayed as the floor of the current
* value. */
/* A quality that is always displayed as the nearest integer of
* the current value, rounded down. Options (in the opts
* parameter) are the same as for QualityDefinition. */
var IntegerQuality = function(title, opts) {
QualityDefinition.call(this, title, opts);
};
@ -152,23 +222,35 @@
/* A quality that displays its full numeric value, including
* decimal component. This is actually a trivial wrapper around
* the QualityDefinition class, which formats in the same way. */
* the QualityDefinition class, which formats in the same
* way. Options (in the opts parameter) are the same as for
* QualityDefinition. */
var NumericQuality = function(title, opts) {
QualityDefinition.call(this, title, opts);
};
NumericQuality.inherits(QualityDefinition);
/* A quality that displays its values as one of a set of
* words. These map to the integer component of the corresponding
* quality value. With offset=0 (the default), then a value of 0
* will map to the first word, and so on. If offset is non-zero
* then the value given will correspond to the first word in the
* list. So if offset=4, then the first word in the list will be
* used for value=4. Words outside the range of the values given
* will be constructed from the limits of the values given and an
* integer modifier. So if the words are 'low', 'high' with no
* offset, a value of 2 will be 'high+1' and -2 will be
* 'low-2'. */
* words. The quality value is first rounded down to the nearest
* integer, then this value is used to select a word to
* display. The offset parameter (optionally passed in as part of
* the opts object) controls what number maps to what word.
*
* The following options (in the opts parameter) are available:
*
* offset - With offset=0 (the default), the quantity value of 0
* will map to the first word, and so on. If offset is
* non-zero then the value given will correspond to the first
* word in the list. So if offset=4, then the first word in
* the list will be used for value=4.
*
* Other options are the same as for QualityDefinition.
*
* Words outside the range of the values given will be constructed
* from the limits of the values given and an integer modifier. So
* if the words are 'low', 'high' with no offset, a value of 2
* will be 'high+1' and -2 will be 'low-2'.
*/
var WordScaleQuality = function(title, values, opts) {
var myOpts = $.extend({
offset: null
@ -192,7 +274,11 @@
};
/* An on/off quality that removes itself from the quality list if
* it is not present. */
* it has a zero value. If it has a non-zero value, its value
* field is usually left empty, but you can specify your own
* string to display as the `onValue` parameter of the opts
* object. Other options (in the opts parameter) are the same as
* for QualityDefinition. */
var OnOffQuality = function(title, opts) {
var myOpts = $.extend({
onValue: ""
@ -207,7 +293,8 @@
};
/* Defines a group of qualities that should be displayed together,
* before any miscellaneous qualities. */
* before any miscellaneous qualities. These should be defined in
* the `undum.game.qualityGroups` parameter. */
var QualityGroup = function(title, opts) {
var myOpts = $.extend({
priority: title,
@ -218,30 +305,66 @@
this.extraClasses = myOpts.extraClasses;
};
// -----------------------------------------------------------------------
// Internal Types.
// Types Passed to Situations
// -----------------------------------------------------------------------
/* The interface from Undum to situations. */
/* A system object is passed into the enter, act and exit
* functions of each situation. It is used to interact with the
* UI. */
var System = function () {
};
/* Outputs regular content to the page. The content supplied MUST
* begin and end with HTML start/end tags. You could have several
* paragraphs, however, as long as the content starts with the <p>
* of the first paragraph, and ends with the </p> of the last. */
/* Outputs regular content to the page. The content supplied must
* be valid "Display Content".
*
* "Display Content" is any HTML string that begins with a HTML
* start tag, ends with either an end or a closed tag, and is a
* valid and self-contained snippet of HTML. Note that the string
* doesn't have to consist of only one HTML tag. You could have
* several paragraphs, for example, as long as the content starts
* with the <p> of the first paragraph, and ends with the </p> of
* the last. So "<p>Foo</p><img src='bar'>" is valid, but "foo<img
* src='bar'>" is not.
*/
System.prototype.write = function(content) {
var output = augmentLinks(content);
$('#content').append(output);
var content = $('#content').append(output);
};
/* Call this method before doing a chunk of writing, so that the
* client will elegantly scroll to that location. This doesn't
* happen automatically, because you may want to write several
* chunks in one go, and it would be annoying to scroll to the
* bottom of those. */
System.prototype.scrollHere = function() {
setTimeout(function() {
var body = $("body,html");
var content = $("#content");
body.animate(
{scrollTop:content.scrollTop() + content.height()},
500
);
}, 0);
};
/* Begins a new heading on the page. You could write headings
* using write, manually wrapping them in the appropriate
* HTML. But it is strongly recommended that you use this method,
* as in the future headings may receive additional processing for
* indexing and javascript hooks. Do not wrap the content you pass
* into this function in a HTML heading tag. That will be done for
* you. The extraClasses parameter is there if you need to give
* the resulting heading additional CSS classes; it should be an
* array of strings. */
* HTML. But it is strongly recommended that you use this method.
* In the future headings may receive additional processing for
* indexing and javascript hooks.
*
* There is no need to return "Display Cotnent" from this method,
* any content will do. Do not wrap the content you pass into this
* function in a HTML heading tag. That will be done for you. You
* can, however, use other tags, such as <em> and <span> in your
* heading.
*
* The extraClasses parameter is there if you need to give the
* resulting heading additional CSS classes; it should be an array
* of strings.
*/
System.prototype.writeHeading = function(content, extraClasses) {
var h = $("<h1>").html(content);
if (extraClasses) {
@ -253,13 +376,15 @@
};
/* Call this to change the character text: the text in the right
* toolbar before the qualities list. The value you give can
* contain links to situations.
* toolbar before the qualities list. This text is designed to be
* a short description of the current state of your character. The
* content you give should be "Display Content" (see
* `System.prototype.write` for the definition).
*/
System.prototype.setCharacterText = function(content) {
var block = $("#character_text_content");
var oldContent = block.html();
var newContent = augmentLinks($("<div>").html(content));
var newContent = augmentLinks(content);
if (block.is(':visible')) {
block.fadeOut(250, function() {
block.html(newContent);
@ -271,10 +396,10 @@
}
};
/* Call this to change the value of a character quality. Don't do
* this by directly changing the quality, because that will not
* update the UI. Because the character's sandbox isn't displayed,
* you can modify that directly. */
/* Call this to change the value of a character quality. Don't
* directly change quality values, because that will not update
* the UI. (You can change any data in the character's sandbox
* directly, however, since that isn't displayed). */
System.prototype.setQuality = function(quality, newValue) {
var oldValue = character.qualities[quality];
character.qualities[quality] = newValue;
@ -315,9 +440,42 @@
}
showHighlight(qualityBlock);
};
/* Changes a quality to a new value, but also shows an animation
* of the change. This probably only makes sense for qualities
* that are numeric. */
/* Changes a quality to a new value, but also shows a progress bar
* animation of the change. This probably only makes sense for
* qualities that are numeric, especially ones that the player is
* grinding to increase. The quality and newValue parameters are
* as for setQuality. The progress bar is controlled by the
* following options in the opts parameter:
*
* from - The proportion along the progress bar where the
* animation starts. Defaults to 0, valid range is 0-1.
*
* to - The proportion along the progress bar where the
* animation ends. Defaults to 1, valid range is 0-1.
*
* showValue - If true (the default) then the new value of the
* quality is displayed above the progress bar.
*
* displayValue - If this is given, and showValue is true, then
* the displayValue is used above the progress bar. If this
* isn't given, and showValue is true, then the display value
* will be calculated from the QualityDefinition, as
* normal. This option is useful for qualities that don't have
* a definition, because they don't normally appear in the UI.
*
* title - The title of the progress bar. If this is not given,
* then the title of the quality is used. As for displayValue
* this is primarily used when the progress bar doesn't have a
* QualityDefinition, and therefore doesn't have a title.
*
* leftLabel, rightLabel - Underneath the progress bar you can
* place two labels at the left and right extent of the
* track. These can help to give scale to the bar. So if the
* bar signifies going from 10.2 to 10.5, you might label the
* left and right extents with "10" and "11" respectively. If
* these are not given, then the labels will be omitted.
*/
System.prototype.animateQuality = function(quality, newValue, opts) {
// Overload default options.
var myOpts = $.extend({
@ -391,12 +549,124 @@
this.setQuality(quality, newValue);
};
/* Constructor for character data. */
/* The character that is passed into each situation is of this
* form.
*
* The `qualities` data member maps the Ids of each quality to its
* current value. When implementing enter, act or exit functions,
* you should consider this to be read-only. Make all
* modifications through `System.prototype.setQuality`, or
* `System.prototype.animateQuality`. In your `init` function, you
* can set these values directly.
*
* The `sandbox` data member is designed to allow your code to
* track any data it needs to. The only proviso is that the data
* structure should be serializable into JSON (this means it must
* only consist of primitive types [objects, arrays, numbers,
* booleans, strings], and it must not contain circular series of
* references). The data in the sandbox is not displayed in the
* UI, although you are free to use it to create suitable output
* for the player..
*/
var Character = function() {
this.qualities = {};
this.sandbox = {};
};
/* The data structure holding the content for the game. By default
* this holds nothing. It is this data structure that is populated
* in the `.game.js` file. Each element in the structure is
* commented, below.
*
* This should be static data that never changes through the
* course of the game. It is never saved, so anything that might
* change should be stored in the character.
*/
var game = {
// Situations
/* An object mapping from the unique id of each situation, to
* the situation object itself. This is the heart of the game
* specification. */
situations: {},
/* The unique id of the situation to enter at the start of a
* new game. */
start: "start",
// Quality display definitions
/* An object mapping the unique id of each quality to its
* QualityDefinition. You don't need definitions for every
* quality, but only qualities in this mapping will be
* displayed in the character box of the UI. */
qualities: {},
/* Qualities can have an optional group Id. This maps those
* Ids to the group definitions that says how to format its
* qualities.
*/
qualityGroups: {},
// Hooks
/* This function is called at the start of the game. It is
* normally overridden to provide initial character creation
* (setting initial quality values, setting the
* character-text. This is optional, however, as set-up
* processing could also be done by the first situation's
* enter function. If this function is given it should have
* the signature function(character, system).
*/
init: null,
/* This function is called before entering any new
* situation. It is called before the corresponding situation
* has its `enter` method called. It can be used to implement
* timed triggers, but is totally optional. If this function
* is given it should have the signature:
*
* function(character, system, oldSituationId, newSituationId);
*/
enter: null,
/* This function is called before carrying out any action in
* any situation. It is called before the corresponding
* situation has its `act` method called. If this optional
* function is given it should have the signature:
*
* function(character, system, situationId, actionId);
*/
beforeAction: null,
/* This function is called after carrying out any action in
* any situation. It is called after the corresponding
* situation has its `act` method called. If this optional
* function is given it should have the signature:
*
* function(character, system, situationId, actionId);
*/
afterAction: null,
/* This function is called after leaving any situation. It is
* called after the corresponding situation has its `exit`
* method called. Like that method, it shoudld return true if
* it wants the transition to go ahead, or false to stop
* it. If this optional function is given it should have the
* signature:
*
* function(character, system, oldSituationId, newSituationId);
*/
exit: null
};
// =======================================================================
// Code below doesn't form part of the public API for UNDUM, so
// you shouldn't find you need to use it.
// -----------------------------------------------------------------------
// Internal Data Structures
@ -405,20 +675,6 @@
/* The global system object. */
var system = new System();
/* The data structure holding the content for the game. This
* should be static data. Anything that might change should be
* stored in the character. */
var game = {
start: "start",
situations: {},
/* We can define these functions to do global processing. */
init: null,
enter: null,
beforeAction: null,
afterAction: null,
exit: null
};
/* This is the character data that gets saved. It isn't the
* character that the situations see, it holds other internal data
* too. */
@ -569,7 +825,17 @@
if (action) {
var situation = getCurrentSituation();
if (situation) {
if (game.beforeAction) {
game.beforeAction(
character, system, sysChar.current, action
);
}
situation.act(character, system, action);
if (game.afterAction) {
game.afterAction(
character, system, sysChar.current, action
);
}
}
}
@ -585,9 +851,13 @@
// Notify the exiting situation, exit if we've finished or if
// we're not allowed to enter the new situation.
if (!oldSituation || !oldSituation.exit(
character, system, newSituationId
)) return;
if (!oldSituation) return;
if (!oldSituation.exit(character, system, newSituationId)) return;
if (game.exit) {
if (!game.exit(character, system, oldSituationId, newSituationId)){
return;
}
}
// Remove links and transient sections.
$('#content a').each(function (index, element) {
@ -602,6 +872,9 @@
// Notify the incoming situation, unless we're ending.
if (newSituation) {
if (game.enter) {
game.enter(character, system, oldSituationId, newSituationId);
}
newSituation.enter(character, system, oldSituationId);
}
};
@ -660,6 +933,9 @@
var situation = getCurrentSituation();
// assert(situation);
if (game.enter) {
game.enter(character, system, null, sysChar.current);
}
situation.enter(character, system, null);
showQualities();
};
@ -693,7 +969,7 @@
};
// -----------------------------------------------------------------------
// Setup and API
// Setup
// -----------------------------------------------------------------------
/* Export our API. */

View file

@ -44,9 +44,9 @@
<div class="label">
<!-- Game Title: edit this -->
<h1>The Instant Alchemist <span class='fancy'>&amp;</span><br>
His Sanguine Servant</h1>
<h2>by Ian Duncan</h2>
<h1>The Undum Tutorial <span class='fancy'>&amp;</span><br>
Interactive Example</h1>
<h2>by I D Millington</h2>
<!-- End of Game Title -->
<p class="click_message">click to begin</p>