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:
parent
e38ea7b9cc
commit
823742e559
22
LICENSE
Normal file
22
LICENSE
Normal 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
140
README.md
Normal 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.
|
|
@ -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:"✓"}
|
||||
novice: new undum.OnOffQuality(
|
||||
"Novice", {priority:"0002", group:'progress', onValue:"✓"}
|
||||
)
|
||||
};
|
||||
|
||||
|
@ -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.");
|
||||
};
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -44,9 +44,9 @@
|
|||
<div class="label">
|
||||
|
||||
<!-- Game Title: edit this -->
|
||||
<h1>The Instant Alchemist <span class='fancy'>&</span><br>
|
||||
His Sanguine Servant</h1>
|
||||
<h2>by Ian Duncan</h2>
|
||||
<h1>The Undum Tutorial <span class='fancy'>&</span><br>
|
||||
Interactive Example</h1>
|
||||
<h2>by I D Millington</h2>
|
||||
<!-- End of Game Title -->
|
||||
|
||||
<p class="click_message">click to begin</p>
|
Loading…
Reference in a new issue