wiki rule

This commit is contained in:
p.kosyh 2010-01-26 08:02:55 +00:00
parent 955e2f0b8f
commit eb9057d66d
6 changed files with 427 additions and 357 deletions

View file

@ -25,7 +25,8 @@ tarball: clean svnclean
$(RM) -f Rules.make
ln -sf Rules.make.standalone Rules.make
ln -sf ./ $(VERTITLE)
make -C doc/
make pdf -C doc/
make wiki -C doc/
man doc/instead.6 > doc/instead.txt
tar -cz --exclude $(VERTITLE)/$(VERTITLE) --exclude .svn --exclude $(ARCHIVE) -f $(ARCHIVE) $(VERTITLE)/*
$(RM) -f $(VERTITLE)

View file

@ -6,6 +6,14 @@ pdf:
makeindex manual.idx
pdflatex manual.tex
$(RM) -f manual.aux manual.log manual.toc manual.out manual.idx manual.ind manual.ilg
wiki:
wget "http://instead.pinebrush.com/wiki/ru/doku.php?id=doc-ru&do=export_xhtml" -O writing_games.html
wget "http://instead.pinebrush.com/wiki/en/doku.php?id=doc-en&do=export_xhtml" -O writing_games-en.html
wget "http://instead.pinebrush.com/wiki/ru/doku.php?id=doc-ru&do=export_raw" -O writing_games.txt
wget "http://instead.pinebrush.com/wiki/en/doku.php?id=doc-en&do=export_raw" -O writing_games-en.txt
clean:
all:
install:

View file

@ -28,6 +28,6 @@ Official game forum: <a href="http://instead.pinebrush.com/">http://instead.pine
<hr>
<center>[ <a href="#info">What is it?</a> ] [ <a href="#screenshots">Screenshots</a> ] [ <a href="#download"> Download </a> ]</center>
<p align = "right">(c)by Peter Kosyh, a.k.a. gl00my '2009 (gl00my(dog)mail.ru, jabber:gl00my@jabber.ru)</p>
<p align = "right">(c)by Peter Kosyh, a.k.a. gl00my '2009-2010 (gl00my(dog)mail.ru, jabber:gl00my@jabber.ru)</p>
</body></html>

View file

@ -29,6 +29,6 @@
<hr>
<center>[ <a href="#info">Что это такое?</a> ] [ <a href="#screenshots">Скриншоты</a> ] [ <a href="#download"> Скачать </a> ]</center>
<p align = "right">(c)by Peter Kosyh, a.k.a. gl00my '2009 (gl00my(dog)mail.ru, jabber:gl00my@jabber.ru)</p>
<p align = "right">(c)by Peter Kosyh, a.k.a. gl00my '2009-2010 (gl00my(dog)mail.ru, jabber:gl00my@jabber.ru)</p>
</body></html>

View file

@ -1,4 +1,4 @@
== 0. General information ==
===== 0. General information =====
Game code for STEAD is written in lua (5.1), therefore it is useful to know the language, though not necessary. The engine code in lua is about ~2000 lines long. And it is the best documentation.
@ -24,21 +24,21 @@ Possible actions of the player are:
Each game is a directory with a “main.lua” script. Other game resources (lua scripts, images, music) should be in this directory. All references to the resources are given relative to this game directory.
At the beginning of “main.lua” file a header may be defined. It consists of tags. Each tag should start with '--' symbols — lua comments. Right now only one tag exists: “$Name:”. It should contain the name of the game in UTF-8 encoding. For example:
{{{
<code>
-- $Name: The most interesting game!$
}}}
</code>
The graphic interpreter searches for available games in the “games” directory. Unix version also checks “~/.instead/games”. Windows version (>=0.8.7) checks “Documents and Settings/USER/Local Settings/Application Data/instead/games”.
== 1. Scene ==
===== 1. Scene =====
A scene is a game unit. Within it a player can examine all the scene objects and interact with them. A game should contain at least one scene with the name “main”.
{{{
<code>
main = room {
nam = 'main room',
dsc = 'You are in a large room.',
};
}}}
</code>
The record means creation of an object “main” of a type “room”. Every object has attributes and handlers. For example the attribute “nam” (name) is obligatory for every object.
The “nam” attribute for a scene will be the scene name when it is played. The name of a scene is also used to identify it when passing between scenes.
@ -46,36 +46,36 @@ The “nam” attribute for a scene will be the scene name when it is played. Th
The “dsc” attribute is a description of a static part of the scene. It is shown once when entering the scene or after the explicit “look” command.
Attention!!! If your creative design requires the static part description to be shown every time, you may define the “forcedsc” parameter for your game (at the start).
{{{
<code>
game.forcedsc = true;
}}}
</code>
Or similarly set the “forcedsc” for particular scenes.
For long descriptions the following format is convenient:
{{{dsc = [[ Very long description... ]],}}}
<code>dsc = [[ Very long description... ]],</code>
In this format line breaks are ignored. If you need paragraph breaks in the description, use the “^” symbol.
{{{
<code>
dsc = [[ First paragraph. ^^
Second paragraph.^^
Third paragraph.^
New line.]],
}}}
</code>
== 2. Objects ==
===== 2. Objects =====
Objects are units of a scene, with which the player interacts.
{{{
<code>
tabl = obj {
nam = 'table',
dsc = 'There is a {table} in the room.',
act = 'Hm... Just a table...',
};
}}}
</code>
Object name “nam” is used when the object gets into the inventory or to address the object in a text interpreter.
“dsc” is an object descriptor. It will be shown in the dynamic part of the scene. Curly brackets indicate the text fragment which will be a link anchor in the graphic interpreter.
@ -85,25 +85,25 @@ Object name “nam” is used when the object gets into the inventory or to addr
WARNING: in the lua namespace some objects (tables) already exist. For example “table”, “io”, “string”... Be careful when creating objects. In the example above “tabl” is used instead of “table”.
== 3. Adding objects to the scene ==
===== 3. Adding objects to the scene =====
A reference to an object is a text string, with the object name at its creation. For example 'tabl' is a reference to the object “tabl”.
To place objects to the scene one has to define the “obj” array of references to objects:
{{{
<code>
main = room {
nam = 'main room',
dsc = 'You are in a large room.',
obj = { 'tabl' },
};
}}}
</code>
This way when the scene is shown we'll see the table object in the dynamic part.
== 4. Objects referencing objects ==
===== 4. Objects referencing objects =====
Objects may contain “obj” attribute too. This way the list will expand sequentially. For example let's place an apple on the table.
{{{
<code>
aple = obj {
nam = 'apple',
dsc = 'There is an {apple} on the table.',
@ -116,23 +116,23 @@ tabl = obj {
act = 'Hm... Just a table...',
obj = { 'aple' },
};
}}}
</code>
This way in the scene description we'll see descriptions of objects “table” and “apple”, because “aple” is an object referenced by tabl.
/* Translator's note: in the English version of the document I renamed the “apple” variable to “aple” to distinguish it from aple.nam */
== 5. Attributes and handlers as functions ==
===== 5. Attributes and handlers as functions =====
Most attributes and handlers may also be functions. For example:
{{{
<code>
nam = function()
return 'apple';
end,
}}}
</code>
This is synonymous to: nam = 'apple';
Functions greatly enhance STEAD capabilities, for example:
{{{
<code>
aple = obj {
nam = 'apple',
dsc = function(s)
@ -151,11 +151,11 @@ aple = obj {
end
end,
};
}}}
</code>
If the attribute or handler is laid out as a function, then the first argument of the function (s) is the object itself. In the example scene the dynamic part will have the text: 'There is something on the table.' When you try to use this “something”, '_seen' variable of the object “aple” will be set to “true” and we will see it was an apple.
If you know lua, you can simplify:
{{{
<code>
function sw(v, a, b)
if v then
return a;
@ -179,7 +179,7 @@ aple = obj {
end
end,
};
}}}
</code>
And then always use “sw” or some other auxiliary function.
`s._seen` means that the `_seen` variable is placed in the “s” object (in our case “aple”). Underscore means that this variable is saved in a savegame file. Starting from version 0.7.7 variables starting with a capital letter also get saved.
@ -187,13 +187,13 @@ And then always use “sw” or some other auxiliary function.
Warning!!! The variables outside any of the following object types: room, object, game, player — never get saved.
From version 0.8.9 you can define a function “isForSave(k)”, which is called to determine whether to save a variable to a savegame file. By default it is defined this way:
{{{
<code>
function isForSave(k)
return string.find(k, '_') == 1 or string.match(k,'^%u')
}}}
</code>
Sometimes we need a handler that would do something without showing any description, e.g.:
{{{
<code>
button = obj {
nam = "button",
dsc = "There is a big red {button} on the room wall.",
@ -202,18 +202,18 @@ button = obj {
return true;
end,
}
}}}
</code>
In this case `act` handler is used to change room description but it is not supposed to add any description of its own. To achieve this we need to return true from the handler. It means the action is done successfully but does not require to diplay any additional description.
If you need to show some action is impossible, you can return false or nil from its `act` handler. In this case default description will be shown for this action. Default actions can be set via game.act handler and are generally used for description of impossible actions.
== 6. Inventory ==
===== 6. Inventory =====
The easiest way to create a takeable object is to define a “tak” handler.
For example:
{{{
<code>
aple = obj {
nam = 'apple',
dsc = 'There is an {apple} on the table.',
@ -223,15 +223,15 @@ aple = obj {
end,
tak = 'You take the apple.',
};
}}}
</code>
This way when the player uses the “apple” object the apple is removed from the scene and added to the inventory. When the player uses the inventory “inv” handler is called.
In the present example when the player uses the apple in the inventory, the apple is eaten.
== 7. Passing between the scenes ==
===== 7. Passing between the scenes =====
To pass from one scene to another use the scene attribute — the “way” list.
{{{
<code>
room2 = room {
nam = 'hall',
dsc = 'You are in a huge hall.',
@ -245,11 +245,11 @@ main = room {
obj = { 'tabl' },
way = { 'room2' },
};
}}}
</code>
This way you can pass between ”main” and “room2” scenes. As you remember, “nam” may be a function, and you can generate scene names on the fly. For example if you don't want the player to know the name of the scene until he gets there.
When switching between scenes the engine calls the “exit” handler from the current scene and the “enter” from the destination scene. For example:
{{{
<code>
room2 = room {
enter = 'You enter the hall.',
nam = 'hall',
@ -257,10 +257,10 @@ room2 = room {
way = { 'main' },
exit = 'You leave the hall.',
};
}}}
</code>
“exit” and “enter” may be functions. Then the first parameter is the object itself (as usual) and the second parameter is a reference to the room where the player is heading (for “exit”) or which he is leaving (for “enter”). For example:
{{{
<code>
room2 = room {
enter = function(s, f)
if f == 'main' then
@ -276,15 +276,15 @@ room2 = room {
end
end,
};
}}}
</code>
As we see, the handlers can return two values: the string and the status. In our example the “exit” function returns “false” if the player tries to go to the “main” room from the hall. “false” means that the player will not pass. Same logic works for “enter” and “tak”.
== 8. Using an object on an object ==
===== 8. Using an object on an object =====
The player may use an inventory object on other objects. In this case “use” handler is invoked for the object in the inventory and “used” for the other one.
For example:
{{{
<code>
knife = obj {
nam = 'knife',
dsc = 'There is a {knife} on the table',
@ -300,13 +300,13 @@ tabl = obj {
obj = { 'aple', 'knife' },
used = 'You try to do something with the table...',
};
}}}
</code>
If the player takes the knife and uses it on the table, he gets the text of “use” and “used” hanlers. “use” and “used” may be functions. Then the first parameter is the object itself. The second parameter for “use” is the object being subjected to the action and fot “used” is the object performing the action.
If “use” returns “false” status, then “used” is not invoked (if there is one). The status of “used” is ignored.
Example:
{{{
<code>
knife = obj {
nam = 'knife',
dsc = 'There is a knife on the {table}',
@ -319,26 +319,26 @@ knife = obj {
return 'You incise your initials on the table.';
end
};
}}}
</code>
You can use the knife only on the table.
== 9. Player object ==
===== 9. Player object =====
In STEAD the player is represented by the object “pl”. The object type is “player”. In the engine it's created thie way:
{{{
<code>
pl = player {
nam = "Incognito",
where = 'main',
obj = { }
};
}}}
</code>
The “obj” attribute represents the player's inventory.
== 10. The object “game” ==
===== 10. The object “game” =====
The game is also represented by the object “game” of type “game”. In the engine it is defined this way:
{{{
<code>
game = game {
nam = "INSTEAD -- Simple Text Adventure interpreter v"..version.." '2009 by Peter Kosyh",
dsc = [[
@ -348,20 +348,20 @@ Commands:^
pl ='pl',
showlast = true,
};
}}}
</code>
As we can see, the object keeps the reference to the current player ('pl') and some parameters. For example at the start of your game you can set the encoding the following way:
{{{ game.codepage="UTF-8"; }}}
<code> game.codepage="UTF-8"; </code>
The support of arbitrary encodings is present in every UNIX version of the interpreter and in windows versions from 0.7.7.
Also the object “game” may contain the default handlers: “act”, “inv”, “use”. They will be invoked if no other handlers are found after the user's actions. For example you can write at the game start:
{{{
<code>
game.act = 'You can\'t.';
game.inv = 'Hmm... Odd thing...';
game.use = 'Won\'t work...';
}}}
</code>
== 11. Attribute lists ==
===== 11. Attribute lists =====
Attribute lists (such as “way” or “obj”) allow to work with themselves thus allowing to implement dynamically defined passages between scenes, live objects, etc.
@ -370,16 +370,16 @@ List methods are: “add”, “del”, “look”, “srch”. The most used ar
“add” adds to the list, “del” removes from the list, “srch” performs a search. Note that “del” and “srch” may use as a parameter not only the object itself or its identifier, but also the object name.
Starting from version 0.8 the object itself may be a parameter of “add”. Also from this version an optional second parameter is added — position in list. From 0.8 you also can modify the list by the index with the “set” method. For example:
{{{
<code>
objs():set('knife',1);
}}}
</code>
You've seen the above example with the eaten apple. It used inv():del('aple');
“inv()” is a function, which returns the inventory list. “del” after “:” is a method, that deletes an element of the inventory.
Similarly, “tak” may be implemented this way:
{{{
<code>
knife = obj {
nam = 'knife',
dsc = 'There is a {knife} on the table,
@ -389,7 +389,7 @@ knife = obj {
inv():add('knife');
end,
};
}}}
</code>
Apart from adding and deleting objects from lists you may switch them on and off with “enable()” and “disable()” methods. E.g. “knife:disable()”. This way the object “knife” will disappear from the scene description, but may be switched on later with “knife:enable()”.
@ -397,7 +397,7 @@ Apart from adding and deleting objects from lists you may switch them on and off
From version 0.9.1 methods “zap” and “cat” can be used. zap() -- delete all elements. cat(b, [pos]) -- add all elements of list b to current list at position [pos].
== 12. Functions, that return objects ==
===== 12. Functions, that return objects =====
In STEAD several functions are defined, that return some frequently used objects. For example:
* inv() returns the inventory list;
@ -408,44 +408,44 @@ In STEAD several functions are defined, that return some frequently used objects
* from() returns an object from a previous scene;
Combining those functions with “add” and “del” methods one can dynamically alter the scene, for example:
{{{
<code>
ways():add('nexroom'); — add a passage to a new scene;
}}}
{{{
</code>
<code>
objs():add('chair'); — add an object to the current scene;
}}}
</code>
Another function gets an object by reference:
ref().
For example we can add an object to the 'home' location like this:
{{{
<code>
ref('home').obj:add('chair');
}}}
</code>
This shorter variant is also correct:
{{{
<code>
home.obj:add('chair');
}}}
</code>
Or, for version >=0.8.5:
{{{
<code>
objs('home'):add('chair');
}}}
</code>
and finally:
{{{
<code>
put('chair', 'home');
}}}
</code>
From 0.8.5 deref(o), returns the reference-string for an object;
== 13. Some auxiliary functions. ==
===== 13. Some auxiliary functions. =====
STEAD has a number of high-level functions, that may come useful when writing games.
have() — checks if the object is in the inventory by object name or by object “nam” attribute. For example:
{{{
<code>
...
act = function(s)
if have('knife') then
@ -453,24 +453,24 @@ act = function(s)
end
end
...
}}}
</code>
move(o, w) — moves an object from the current scene to another:
{{{ move('mycat','inmycar');}}}
<code> move('mycat','inmycar');</code>
If you want to move an object from an arbitrary scene, you'll have to delete it from the original scene with the “del” method. To create objects, that move in complex ways, you'll have to write a method that would save the object's position in the object itself and delete it from the original scene. You can set the initial position (room) as the third parameter of “move”.
{{{ move('mycat','inmycar', 'forest'); }}}
<code> move('mycat','inmycar', 'forest'); </code>
From version 0.8 there is a “movef” function similar to “move”, but adding the object to the start of the list.
seen(o) — is object present in the current scene:
{{{
<code>
if seen('mycat') then
move('mycat','inmycar');
end
}}}
</code>
From 0.8.6 has an optional second parameter — the scene.
drop(o) — drop an object from the inventory to the scene:
@ -495,7 +495,7 @@ goto(w) — go to scene w, the handler has to return the “goto” return value
change_pl(p) — switch to another player (with one's own inventory and position). The function returns the scene description of the new player and the returned value has to be transferred from the handler (see “goto()”).
{{{
<code>
mycar = obj {
nam = 'my car',
dsc = 'In front of the cabin there is my old Toyota {pickup}.',
@ -503,7 +503,7 @@ mycar = obj {
return goto('inmycar');
end
};
}}}
</code>
back() — “goto” to the previous scene.
@ -513,10 +513,10 @@ cat(...) — returns the string, concatenating argument strings. If the first ar
par(...) — returns the string, concatenating argument strings split by the first argument string.
== 14. Dialogs ==
===== 14. Dialogs =====
Dialogs are scenes with phrase objects. The simplest dialog may look like this:
{{{
<code>
povardlg = dlg {
nam = 'in the kitchen',
dsc = 'I see a fat face of a lady cook wearing a white hat with a tired look...',
@ -527,10 +527,10 @@ povardlg = dlg {
[4] = phr('“Something light, please, I've got an ulcer...”', '“Oatmeal!”'),
},
};
}}}
</code>
“phr” creates a phrase. A phrase contains a question, an answer and a reaction (the example has no reaction). When the player picks one of the phrases, it is disabled. When all phrases are disabled, the dialog is over. Reaction is a line of lua code, which is executed when the phrase is disabled. E.g.:
{{{
<code>
food = obj {
nam = 'food',
inv = function (s)
@ -555,7 +555,7 @@ povardlg = dlg {
[4] = phr('“Something light, please, I've got an ulcer...”', '“Oatmeal!”', [[pon(4); return gotfood(4);]]),
},
};
}}}
</code>
In the example the player chooses his dinner. After getting the food (recording the choice in the “food._num” variable) he returns back to the scene from where he got in the dialog.
The reaction may have any lua code, but STEAD has some frequently used functions predefined:
@ -565,7 +565,7 @@ poff(n...) — disable the phrases with numbers n...
prem(n...) — remove (block) phrases with numbers n... (blocked phrases won't be re-enabled with subsequent “pon”).
Player enters a dialog the way he enters a scene:
{{{
<code>
povar = obj {
nam = 'cook',
dsc = 'I see a {cook}.',
@ -573,11 +573,11 @@ povar = obj {
return goto('povardlg');
end,
};
}}}
</code>
You can pass from one dialog to another, organizing hierarchic dialogs.
You can also hide some phrases when initializing the dialog and show them under certain conditions.
{{{
<code>
facectrl = dlg {
nam = 'фэйсконтроль',
dsc = 'I see an unpleasant face of a fat guard.',
@ -597,16 +597,16 @@ facectrl = dlg {
s:pon(1);
end,
};
}}}
</code>
`_phr` — creates a disabled phrase, which can be enabled. The example also shows the use of “pon”, “poff”, “prem” methods for a dialog (see “exit”).
You can enable/disable phrases not only of the current put of any arbitrary dialog with the “pon”/“poff” methods of a dialog object. For example: shopman:pon(5);
== 15. Lightweight objects ==
===== 15. Lightweight objects =====
Sometimes a scene has to be filled with decorations with a limited functionality to add variety to the game. For that lightweight objects can be used. For example:
{{{
<code>
sside = room {
nam = 'southern side',
dsc = [[I am near the southern wall of an institute building. ]],
@ -622,7 +622,7 @@ sside = room {
obj = { vobj(1, "porch", "There is a small {porch} by the eastern corner."),
vobj(2, "people", "From time to time the porch door slams letting {people} in and out..")},
};
}}}
</code>
As you see, “vobj” allows to create a lightweight version of a static object, with which it will still be possible to interact (defining an “act” handler in the scene an analyzing the object key). “vobj” also calls the “used” method with the third parameter being the object which acts on the virtual object.
“vobj” syntax: vobj(key, name, descriptor); where key is a number to be transferred to the “act”/“used” handlers of the scene as a second parameter.
@ -630,29 +630,29 @@ As you see, “vobj” allows to create a lightweight version of a static object
There is a modification of “vobj” object — “vway”. It creates a reference.
“vway” syntax: vway(name, descriptor, destination scene); for example:
{{{
<code>
obj = { vway("next", "Press {here}.", 'nextroom') }
}}}
</code>
You can dynamically fill the scene with “vobj” and “vway” objects. Use methods “add” and “del”. For example:
{{{
<code>
home.objs:add(vway("next", "{Next}.", 'next_room');
-- some code here
home.objs:del("next");
}}}
</code>
Also a simplified scene “vroom” is defined.
Syntax: vroom(passage name, destination scene). For example:
{{{
<code>
home.objs:add(vroom("go west", 'mountains');
}}}
</code>
== 16. Dynamic events ==
===== 16. Dynamic events =====
You can define handlers, that would execute every time when the game time increments by 1. E.g.:
{{{
<code>
mycat = obj {
nam = 'Barsik',
lf = {
@ -681,27 +681,27 @@ profdlg2 = dlg {
'I snatch Barsik from Belin's hand and put in my bosom.',
[[inv():add('mycat'); lifeon('mycat')]]),
....
}}}
</code>
Any object or scene may have their “life” handler, which is called every time the game time advances, if the object or the scene have been added to the list of living objects with “lifeon”. Don't forget to remofe living objects from the list with “lifeoff”, when you no longer need them. You can do this, for example, in the “exit” handler or some other way.
life may return string, that will be printed after all text of scene.
From 0.9.1 you can return second retval -- importance. (true or false). For example:
{{{
<code>
return 'Guard entered the room.', true -- The event will be printed before objects description.
}}}
</code>
== 17. Graphics and music ==
===== 17. Graphics and music =====
Graphic interpreter analyzes the scene “pic” attribute and treats it as a path to the picture. For example:
{{{
<code>
home = room {
pic = 'gfx/home.png',
nam = 'at home',
dsc = 'I am at home',
};
}}}
</code>
Of couce, “pic” may be a function. This enhaces the developer's capabilities.
If the current scene has no “pic” attribute defined, the “game.pic” attribute is taken. If “game.pic” isn't defined, no picture is displayed.
@ -710,16 +710,16 @@ From version 0.9.2 you can use animated gif files.
From version 0.9.2 graphics can be embedded everywhere in text or inventory with img function. For example:
{{{
<code>
knife = obj {
nam = 'Knife'..img('img/knife.png'),
}
}}}
</code>
The interpreter cycles the current music defined by the function ”set_music(music file name)”.
For example:
{{{
<code>
street = room {
pic = 'gfx/street.png',
enter = function()
@ -728,13 +728,13 @@ street = room {
nam = 'on the street',
dsc = 'It is raining outside.',
};
}}}
</code>
From version 1.0.0 the interpreter can compose picture from base image and overlays:
{{{
<code>
pic = 'gfx/mycat.png;gfx/milk.png@120,25;gfx/fish.png@32,32'
}}}
</code>
get_music() returns the current track name.
@ -747,9 +747,9 @@ To stop music use stop_music() function (from version 1.0.0).
Use is_music() to check if music is playing. (from version 1.0.0)
== 18. Advices ==
===== 18. Advices =====
=== Formatting ===
==== Formatting ====
You can do simple text formatting with functions:
txtc() - center align;
@ -757,12 +757,12 @@ txtr() - right align;
txtl() - left align;
For example:
{{{
<code>
main = room {
nam = 'Intro',
dsc = txtc('Welcome!'),
}
}}}
</code>
You can define text style with functions:
@ -771,15 +771,15 @@ txtem() - emboss;
txtu() - underline;
For example:
{{{
<code>
main = room {
nam = 'Intro',
dsc = 'You are in the room: '..txtb('main')..'.',
}
}}}
=== Menus ===
</code>
==== Menus ====
You can do menus in the inventory area, using menu constructor. Menu handler will be called after single mouse click. If handler have no return string the state of game will no change. For example, here is pocket realisation:
{{{
<code>
pocket = menu {
State = false,
nam = function(s)
@ -817,11 +817,11 @@ pocket:gen();
main = room {
nam = 'test',
};
}}}
=== Player status ===
</code>
==== Player status ====
The engine code is in the stead.lua file. In your game you can redefine any lua function or object, getting whatever your creative concept needs. It is useful to know the peculiarities of the engine work. For example below is an implementation of player status as a text in the inventory, which cannot be picked.
{{{
<code>
pl.Life = 10;
pl.Power = 10;
@ -830,77 +830,77 @@ status = obj {
};
inv():add('status');
status.object_type = false
}}}
</code>
From version 0.9.1 you can use stat constructor for status.
=== “goto” from the “exit” handler ==
==== “goto” from the “exit” handler ====
If you use “goto” from the “exit” handler, you get stack overflow, because goto would call “exit” again and again. You can prevent it by aadding a check that breaks the recursioon. For example:
{{{
<code>
exit = function(s, t)
if t == 'dialog' then return; end
return goto('dialog');
}}}
</code>
From version 0.9.1 this is done by stead engine.
You can also do “goto” from the “enter” handlers.
=== Dynamically created references. ==
==== Dynamically created references. ====
Dynamically created references can be implemented in various ways. The example below uses “vway” objects. To add a reference one can write:
{{{
<code>
home.obj:add(vway('Road', 'I noticed a {road} going into the forest...', 'forest'));
}}}
</code>
To delete a reference one can use “del” method.
{{{
<code>
home.obj:del('Road');
}}}
</code>
The “srch” method can check if the reference is present in the scene.
{{{
<code>
if not home.obj:srch('Road') then
home.obj:add(vway('Road', 'I noticed a {road} going into the forest...', 'forest'));
end
}}}
</code>
It's convenient to create dynamic references either in the “enter” handler, or in the arbitrary place in the game code, where they are required. If the reference is created in the current scene, the example can be simplified:
{{{
<code>
if not seen('Road') then
objs():add(vway('Road', 'I noticed a {road} going into the forest...', 'forest'));
end
}}}
</code>
Or you can just enable and disable references with “enable()” and “disable()”, for example:
{{{
<code>
objs()[1]:disable();
}}}
</code>
Creating disabled “vobj” and “vway”:
{{{
<code>
obj = {vway('Road', 'I noticed a {road} going into the forest...', 'forest'):disable()},
}}}
</code>
And then enabling them by their index in the “obj” array:
{{{
<code>
objs()[1]:enable();
}}}
</code>
=== Encoding game sources (from version 0.9.3) ===
==== Encoding game sources (from version 0.9.3) ====
If you want hide a game source code, you can encode it with command: “sdl-instead -encode <lua file> [encoded file]” and load encode file from lua with “doencfile”. It's neccessary to keep main.lua as plain text file. So, the recommended scheme is (game is a encoded game.lua ):
main.lua
{{{
<code>
-- $Name: My closed source game!$
doencfile("game");
}}}
</code>
WARNING about using luac compiler:
Do not use lua compiler luac, it produces platform-dependent code!
But game compilation is useful to find errors in the game code.
=== Switching between players ===
==== Switching between players ====
You can create a game with several characters and switch between them from time to time (see “switch_pl”). But you can also use the same trick to switch between different types of inventory.
=== Using the first parameter of a handler ===
==== Using the first parameter of a handler ====
Code example.
{{{
<code>
knife = obj {
nam = 'stone',
dsc = 'There is a {stone} at the edge.',
@ -908,21 +908,21 @@ knife = obj {
objs():del('knife');
return 'I pushed the stone, it fell and flew down...';
end
}}}
</code>
The “act” handler could look simpler:
{{{
<code>
act = function(s)
objs():del(s);
return 'I pushed the stone, it fell and flew down...';
end
}}}
</code>
=== Using “set_music” ===
==== Using “set_music” ====
You can use “set_music” to play sounds setting the second parameter — the cycle counter how many times to play the sound file.
You can write your own music player, creating it from a live object, e.g:
{{{
<code>
-- plays tracks in random order, starting from 2-nd
tracks = {"mus/astro2.mod", "mus/aws_chas.xm", "mus/dmageofd.xm", "mus/doomsday.s3m"}
mplayer = obj {
@ -942,11 +942,11 @@ mplayer = obj {
end,
};
lifeon('mplayer');
}}}
</code>
You can use “get_music_loop” and “get_music” functions to remember the last melody and ren restore it, e.g:
{{{
<code>
function save_music(s)
s.OldMusic = get_music();
s.OldMusicLoop = get_music_loop();
@ -965,13 +965,13 @@ exit = function(s)
end,
-- ....
}}}
</code>
From version 0.8.5 functions “save_music” and “restore_music” are already present in the library.
=== Living objects ===
==== Living objects ====
If your hero needs a friend, one of the ways is the “life” method of that character, that would always bring the object to the player's location:
{{{
<code>
horse = obj {
nam = 'horse',
dsc = 'A {horse} is standing next to me.',
@ -983,22 +983,22 @@ horse = obj {
end,
};
lifeon('horse');
}}}
</code>
=== Debugging ===
==== Debugging ====
To see lua call stack during an error, launch sdl-instead with “-debug” parameter. In Windows version debugging console will be created.
You can debug your game without INSTEAD at all. For example, you can create the following “game.lus” file:
{{{
<code>
dofile("/usr/share/games/stead/stead.lua"); -- path to stead.lua
dofile("main.lua"); -- your game
game:ini();
iface:shell();
}}}
</code>
And launch the game in lua: lua game.lua.
This way the game will work in a primitive shell environment. Useful commands: ls, go, act, use....
== 19. Themes for sdl-instead ==
===== 19. Themes for sdl-instead =====
Graphic interpreter supports theme mechanism. A theme is a directory with the “theme.ini” file inside.
@ -1101,16 +1101,16 @@ snd.click = path to the click sound file (string)
include = theme name (the last component in the directory path) (string)
The theme header may include comments with tags. Right now there is only one tag: “$Name:”, it contains an UTF-8 line with the theme name. E.g.:
{{{
<code>
; $Name:New theme$
; modified "book" theme
include = book
scr.gfx.h = 500
}}}
</code>
The interpreter searches for themes in the “themes” directory. Unix version also checks ~/.instead/themes/ directory. Windows version (>=0.8.7) checks "Documents and Settings/USER/Local Settings/Application Data/instead/themes"
TODO
Full list of objects and methods.
Translation: vopros@pochta.ru
Translation: vopros@pochta.ru

View file

@ -1,4 +1,4 @@
== 0. Общие сведения ==
===== 0. Общие сведения =====
Код игр для STEAD пишется на lua (5.1), поэтому, знание этого языка полезно, хотя и не необходимо. Код движка на lua занимает около ~2000 строк и лучшей документацией является изучение его кода.
@ -14,32 +14,32 @@
Действиями игрока могут быть:
* осмотр сцены;
* действие на объект сцены;
* действие на объект инвентаря;
* действие объектом инвентаря на объект сцены;
* действие объектом инвентаря на объект инвентаря;
* переход в другую сцену;
* осмотр сцены;
* действие на объект сцены;
* действие на объект инвентаря;
* действие объектом инвентаря на объект сцены;
* действие объектом инвентаря на объект инвентаря;
* переход в другую сцену;
Игра представляет из себя каталог, в котором должен находиться скрипт main.lua. Другие ресурсы игры (скрипты на lua, графика и музыка) должны находиться в рамках этого каталога. Все ссылки на ресурсы делаются относительно текущего каталога -- каталога игры.
В начале файла main.lua может быть определен заголовок, состоящий из тегов. Теги должны начинаться с символов '--': комментарий с точки зрения lua. На данный момент существует один тег: $Name:, который должен содержать название игры в кодировке UTF8. Пример использования тега:
{{{
<code>
-- $Name: Самая интересная игра!$
}}}
</code>
Графический интерпретатор ищет доступные игры в каталоге games. Unix версия интерпретатора кроме этого каталога просматривает также игры в каталоге ~/.instead/games.
Windows версия (>=0.8.7): Documents and Settings/USER/Local Settings/Application Data/instead/games.
== 1. Сцена ==
===== 1. Сцена =====
Сцена -- это единица игры, в рамках которой игрок может изучать все объекты сцены и взаимодействовать с ними. В игре должна быть хотя бы одна сцена с именем main.
{{{
<code>
main = room {
nam = 'главная комната',
dsc = 'Вы в большой комнате.',
};
}}}
</code>
Запись означает создание объекта main типа room. У каждого объекта игры есть атрибуты и обработчики. Например, атрибут nam (имя) является необходимым для любого объекта.
Атрибут nam для сцены это то, что будет заголовком сцены при ее отображении. Имя сцены также используется для ее идентификации при переходах.
@ -47,34 +47,34 @@ main = room {
Атрибут dsc это описание статической части сцены, которое выводится один раз при входе в сцену или явном выполнении команды look.
Внимание!!! Если для вашего творческого замысла необходимо, чтобы описание статической части сцены выводилось каждый раз, вы можете определить для своей игры параметр forcedsc (в начале игры).
{{{
<code>
game.forcedsc = true;
}}}
</code>
Или, аналогично, задать атрибут forcedsc для конкретных сцен.
Для длинных описаний удобно использовать запись вида:
{{{dsc = [[ Очень длинное описание... ]],}}}
<code>dsc = [[ Очень длинное описание... ]],</code>
При этом переводы строк игнорируются. Если вы хотите, чтобы в выводе описания сцены присутствовали абзацы -- используйте символ ^.
{{{
<code>
dsc = [[ Первый абзац. ^^
Второй Абзац.^^
Третий абзац.^
На новой строке.]],
}}}
== 2. Объекты ==
</code>
===== 2. Объекты =====
Объекты -- это единицы сцены, с которыми взаимодействует игрок.
{{{
<code>
tabl = obj {
nam = 'стол',
dsc = 'В комнате стоит {стол}.',
act = 'Гм... Просто стол...',
};
}}}
</code>
Имя объекта nam используется при попадании его в инвентарь а также в текстовом интерпретаторе для адресации объекта.
dsc -- описатель объекта. Он будет выведен в динамической части сцены. Фигурными скобками отображается фрагмент текста, который будет являться ссылкой в графическом интерпретаторе.
@ -83,24 +83,24 @@ act -- это обработчик, который вызывается при
ВНИМАНИЕ: в пространстве имен lua уже существуют некоторые объекты (таблицы), например: table, io, string... Будьте внимательны, при создании объекта. Например, в приведенном примере используется tabl, а не table.
== 3. Добавляем объекты в сцену ==
===== 3. Добавляем объекты в сцену =====
Ссылкой на объект называется текстовая строка, содержащая имя объекта при его создании. Например: 'tabl' -- ссылка на объект tabl.
Для того, чтобы поместить в сцену объекты, нужно определить массив obj, состоящий из ссылок на объекты:
{{{
<code>
main = room {
nam = 'главная комната',
dsc = 'Вы в большой комнате.',
obj = { 'tabl' },
};
}}}
</code>
Теперь, при отображении сцены мы увидим объект стол в динамической части.
== 4. Объекты связанные с объектами ==
===== 4. Объекты связанные с объектами =====
Объекты тоже могут содержать атрибут obj. При этом, список будет последовательно разворачиваться. Например, поместим на стол яблоко.
{{{
<code>
apple = obj {
nam = 'яблоко',
dsc = 'На столе лежит {яблоко}.',
@ -113,22 +113,22 @@ tabl = obj {
act = 'Гм... Просто стол...',
obj = { 'apple' },
};
}}}
</code>
При этом, в описании сцены мы увидим описание объектов стол и яблоко, так как apple -- связанный с tabl объект.
== 5. Атрибуты и обработчики как функции ==
===== 5. Атрибуты и обработчики как функции =====
Большинство атрибутов и обработчиков могут быть также функциями. Так, например:
{{{
<code>
nam = function()
return 'яблоко';
end,
}}}
</code>
Это синоним записи: nam = 'яблоко';
Функции сильно расширяют возможности STEAD, например:
{{{
<code>
apple = obj {
nam = 'яблоко',
dsc = function(s)
@ -147,11 +147,11 @@ apple = obj {
end
end,
};
}}}
</code>
Если атрибут или обработчик оформлен как функция, то обычно первый аргумент функции (s) сам объект. В данном примере, при показе сцены будет в динамической части сцены будет текст: 'На столе что-то лежит'. При взаимодействии с 'что-то', переменная `_seen` объекта apple будет установлена в true и мы увидим, что это было яблоко.
Зная lua, можно упростить запись:
{{{
<code>
function sw(v, a, b)
if v then
return a;
@ -175,7 +175,7 @@ apple = obj {
end
end,
};
}}}
</code>
И в дальнейшем всегда использовать функцию sw (или какую-либо другую, вспомогательную функцию).
Запись `s._seen` означает, что переменная `_seen` размещена в объекте s (то-есть apple). Подчеркивание означает, что эта переменная попадет в файл сохранения игры. Начиная с версии интерпретатора 0.7.7 в файл сохранения игры попадают также переменные, название которых начинается с большой буквы.
@ -183,13 +183,13 @@ apple = obj {
Внимание!!! Переменные в любом случае не записываются в файл сохранения, если они не размещены в одном из перечисленных типов объектов: комната, объект, игра, игрок.
Начиная с версии 0.8.9 вы можете определить функцию isForSave(k), которая вызывается для определения необходимости записи переменной в файл сохранения. По умолчанию, функция определена следующим образом:
{{{
<code>
function isForSave(k)
return string.find(k, '_') == 1 or string.match(k,'^%u')
}}}
</code>
Иногда может понадобиться обработчик, который совершал бы некоторое действие, но не выводил никакого описания. Например:
{{{
<code>
button = obj {
nam = "кнопка",
dsc = "На стене комнаты видна большая красная {кнопка}.",
@ -198,18 +198,18 @@ button = obj {
return true;
end,
}
}}}
</code>
В данном случае обработчик `act` нужен для того, чтобы поменять описание комнаты, и не нужно, чтобы чтобы он выводил результат действия. Для отключения результата можно вернуть из обработчика значение true -- это будет означать, что действие успешно выполнено, но не требует дополнительного описания.
Если необходимо показать, что действие невыполнимо, можно вернуть из обработчика `act` значение false или nil. При этом будет отображено описание по умолчанию, заданное с помощью обработчика `game.act`. Обычно описание по умолчанию содержит описание невыполнимых действий.
== 6. Инвентарь ==
===== 6. Инвентарь =====
Простейший вариант сделать объект, который можно брать -- определить обработчик tak.
Например:
{{{
<code>
apple = obj {
nam = 'яблоко',
dsc = 'На столе лежит {яблоко}.',
@ -219,15 +219,15 @@ apple = obj {
end,
tak = 'Вы взяли яблоко.',
};
}}}
</code>
При этом, при действии игрока на объект яблоко -- яблоко будет убрано из сцены и добавлено в инвентарь. При действии игрока на инвентарь -- вызывается обработчик inv.
В нашем примере -- при действии игроком на яблоко в инвентаре - яблоко будет съедено.
== 7. Переходы между сценами ==
===== 7. Переходы между сценами =====
Для перехода между сценами используется атрибут сцены -- список way.
{{{
<code>
room2 = room {
nam = 'зал',
dsc = 'Вы в огромном зале.',
@ -241,11 +241,11 @@ main = room {
obj = { 'tabl' },
way = { 'room2' },
};
}}}
</code>
При этом, вы сможете переходить между сценами main и room2. Как вы помните, nam может быть функцией, и вы можете генерировать имена сцен на лету, например, если вы хотите, чтобы игрок не знал название сцены, пока не попал на нее.
При переходе между сценами движок вызывает обработчик exit из текущей сцены и enter в той сцены, куда идет игрок. Например:
{{{
<code>
room2 = room {
enter = 'Вы заходите в зал.',
nam = 'зал',
@ -253,10 +253,10 @@ room2 = room {
way = { 'main' },
exit = 'Вы выходите из зала.',
};
}}}
</code>
exit и enter могут быть функциями. Тогда первый параметр это (как всегда) сам объект, а второй это ссылка на комнату куда игрок хочет идти (для exit) или из которой уходит (для enter). Например:
{{{
<code>
room2 = room {
enter = function(s, f)
if f == 'main' then
@ -272,15 +272,15 @@ room2 = room {
end
end,
};
}}}
</code>
Как видим, обработчики могут возвращать два значения: строку и статус. В нашем примере функция exit вернет false, если игрок попытается уйти из зала в main комнату. false означает, что переход не будет выполнен. Такая же логика работает и для enter. Кроме того, она работает и для обработчика tak.
== 8. Действие объектов друг на друга ==
===== 8. Действие объектов друг на друга =====
Игрок может действовать объектом инвентаря на другие объекты. При этом вызывается обработчик use у объекта которым действуют и used -- на которого действуют.
Например:
{{{
<code>
knife = obj {
nam = 'нож',
dsc = 'На столе лежит {нож}',
@ -296,14 +296,14 @@ tabl = obj {
obj = { 'apple', 'knife' },
used = 'Вы пытаетесь сделать что-то со столом...',
};
}}}
</code>
Если игрок возьмет нож и использует его на стол -- то увидит текст обработчиков use и used. use и used могут быть функциями. Тогда первый параметр это сам объект,
а второй -- ссылка на объект на который направлено действие в случае use и объект, которым действие осуществляется в случае used.
use может вернуть статус false, в этом случае обработчик used не вызовется (если он вообще был). Статус обработчика used -- игнорируется.
Пример:
{{{
<code>
knife = obj {
nam = 'нож',
dsc = 'На столе лежит {нож}',
@ -316,25 +316,25 @@ knife = obj {
return 'Вы вырезаете на столе свои инициалы.';
end
};
}}}
</code>
Нож можно использовать только на стол.
== 9. Объект игрок ==
===== 9. Объект игрок =====
Игрок в STEAD представлен объектом pl. Тип объекта -- player. В движке объект создается следующим образом:
{{{
<code>
pl = player {
nam = "Incognito",
where = 'main',
obj = { }
};
}}}
</code>
Атрибут obj представляет собой инвентарь игрока.
== 10. Объект game ==
===== 10. Объект game =====
Игра также представлена объектом game с типом game. В движке он определяется следующим образом:
{{{
<code>
game = game {
nam = "INSTEAD -- Simple Text Adventure interpreter v"..version.." '2009 by Peter Kosyh",
dsc = [[
@ -344,20 +344,20 @@ Commands:^
pl ='pl',
showlast = true,
};
}}}
</code>
Как видим, объект хранит в себе указатель на текущего игрока ('pl') и некоторые параметры. Например, вы можете указать в начале своей игры кодировку текста следующим образом:
{{{ game.codepage="UTF-8"; }}}
<code> game.codepage="UTF-8"; </code>
Поддержка произвольных кодировок изначально присутствует в UNIX версии интерпретатора, в windows версии -- начиная с 0.7.7.
Кроме того, объект game может содержать обработчики по умолчанию act, inv, use, которые будут вызваны, если в результате действий пользователя не будут найдены никакие другие обработчики. Например, вы можете написать в начале игры:
{{{
<code>
game.act = 'Не получается.';
game.inv = 'Гм.. Странная штука..';
game.use = 'Не сработает...';
}}}
</code>
== 11. Атрибуты - списки ==
===== 11. Атрибуты - списки =====
Атрибуты списки (такие как way или obj) позволяют работать с собой, таким образом позволяя реализовать динамически определяемые переходы между сценами, живые объекты и т.д.
@ -366,16 +366,16 @@ game.use = 'Не сработает...';
add - добавляет в список. del -- удаляет из него. srch -- выполняет поиск объекта. Следует отметить, что параметром del и srch может быть не только сам объект или идентификатор объекта, но и имя объекта.
Начиная с версии 0.8 параметром add может быть сам объект. Кроме того, с этой версии добавляется необязательный второй параметр -- позиция в списке. Начиная с версии 0.8 вы можете также выполнять модификацию списка по индексу с помощью метода set. Например:
{{{
<code>
objs():set('knife',1);
}}}
</code>
Выше, вы уже видели пример со съеденным яблоком, там использовалась конструкция inv():del('apple');
inv() -- это функция, которая возвращает список инвентаря. del после ':'-- метод, удаляющий элемент инвентаря.
Аналогично, собственная реализация tak может быть такой:
{{{
<code>
knife = obj {
nam = 'нож',
dsc = 'На столе лежит {нож}',
@ -385,7 +385,7 @@ knife = obj {
inv():add('knife');
end,
};
}}}
</code>
Кроме добавления, удаления объектов из списков вы можете использовать выключение/включение объектов с помощью методов enable() и disable(). Например: knife:disable(). При этом объект knife пропадает из описания сцены, но в последствии может быть опять быть включен, с помощью knife:enable().
@ -393,54 +393,54 @@ knife = obj {
Начиная с версии 0.9.1 доступны методы disable_all и enable_all, выключающие и включающие вложенные в объект объекты.
== 12. Функции, которые возвращают объекты ==
===== 12. Функции, которые возвращают объекты =====
В STEAD определены некоторые функции, которые возвращают наиболее часто используемые объекты. Например:
* inv() возвращает список инвентаря;
* objs() возвращает список объектов текущей сцены; (начиная с 0.8.5 -- необязательный параметр -- сцена, для которой возвращается список);
* ways() возвращает список возможных переходов из текущей сцены; (начиная с 0.8.5 -- необязательный параметр -- сцена, для которой возвращается список);
* me() возвращает объект-игрок;
* here() возвращает объект текущую сцену; (начиная с 0.8.5 -- еще одна функция where(obj) -- возвращает сцену на которой находится объект, если он был помещен туда с помощью put/move/drop)
* from() возвращает объект прошлой сцены;
* inv() возвращает список инвентаря;
* objs() возвращает список объектов текущей сцены; (начиная с 0.8.5 -- необязательный параметр -- сцена, для которой возвращается список);
* ways() возвращает список возможных переходов из текущей сцены; (начиная с 0.8.5 -- необязательный параметр -- сцена, для которой возвращается список);
* me() возвращает объект-игрок;
* here() возвращает объект текущую сцену; (начиная с 0.8.5 -- еще одна функция where(obj) -- возвращает сцену на которой находится объект, если он был помещен туда с помощью put/move/drop)
* from() возвращает объект прошлой сцены;
Комбинируя эти функции с методами add, del можно динамически менять сцену, например:
{{{
<code>
ways():add('nexroom'); -- добавить переход на новую сцену;
}}}
{{{
</code>
<code>
objs():add('chair'); -- добавить объект в текущую сцену;
}}}
</code>
Еще одна функция, которая получает объект по ссылке:
ref().
Например, мы можем добавить объект в локацию 'home' следующим образом:
{{{
<code>
ref('home').obj:add('chair');
}}}
</code>
Впрочем, следующая более простая запись тоже является корректной:
{{{
<code>
home.obj:add('chair');
}}}
</code>
Или, для версии >=0.8.5:
{{{
<code>
objs('home'):add('chair');
}}}
</code>
или, наконец:
{{{
<code>
put('chair', 'home');
}}}
</code>
Начиная с 0.8.5 -- deref(o), возвращает ссылку-строку для объекта;
== 13. Некоторые вспомогательные функци. ==
===== 13. Некоторые вспомогательные функци. =====
В STEAD определены некоторые высокоуровневые функции, которые могут оказаться полезными при написании игры.
have() -- проверяет, есть ли объект в инвентаре. По имени объекта или по атрибуту nam объекта. Например:
{{{
<code>
...
act = function(s)
if have('knife') then
@ -448,23 +448,23 @@ act = function(s)
end
end
...
}}}
</code>
move(o, w) -- переносит объект из текущей сцены в другую:
{{{ move('mycat','inmycar');}}}
<code> move('mycat','inmycar');</code>
Если вы хотите перенести объект из произвольной сцены, вам придется удалить его из старой сцены с помощью метода del. Для создания сложно перемещающихся объектов, вам придется написать свой метод, который будет сохранять текущую позицию объекта в самом объекте и делать удаление объекта из старой сцены. Вы можете указать исходную позицию (комнату) объекта в качестве третьего параметра move.
{{{ move('mycat','inmycar', 'forest'); }}}
<code> move('mycat','inmycar', 'forest'); </code>
Начиная с версии 0.8 присутствует также функция movef, аналогичная move, но добавляющая объект в начало списка.
seen(o) -- если объект присутствует в текущей сцене:
{{{
<code>
if seen('mycat') then
move('mycat','inmycar');
end
}}}
</code>
Начиная с 0.8.6 -- необязательный второй параметр -- сцена.
drop(o) -- положить объект из инвентаря на сцену:
@ -489,7 +489,7 @@ goto(w) -- перейти в сцену w, при этом обработчик
change_pl(p) -- переключиться на другого игрока (со своим инвентарем и позицией). При этом функция возвращает описание сцены нового игрока и это возвращаемое значение должно быть передано из обработчика (см. goto()).
{{{
<code>
mycar = obj {
nam = 'моя машина',
dsc = 'Перед хижиной стоит мой старенький {пикап} Toyota.',
@ -497,7 +497,7 @@ mycar = obj {
return goto('inmycar');
end
};
}}}
</code>
back() -- goto в предыдущую сцену.
@ -507,10 +507,10 @@ cat(...) -- возвращает строку -- склейку строк-ар
par(...) -- возвращает строку -- склейку строк-аргументов, разбитых строкой-первым параметром.
== 14. Диалоги ==
===== 14. Диалоги =====
Диалоги это сцены, содержащие объекты -- фразы. Например, простейший диалог может выглядеть следующим образом.
{{{
<code>
povardlg = dlg {
nam = 'на кухне',
dsc = 'Передо мной полное лицо женщины - повара в белом колпаке и усталым взглядом...',
@ -521,9 +521,9 @@ povardlg = dlg {
[4] = phr('Мне что-нибудь легонькое, у меня язва...', 'Овсянка!'),
},
};
}}}
</code>
phr -- создание фразы. Фраза содержит вопрос, ответ и реакцию (реакция в данном примере отсутствует). Когда игрок выбирает одну из фраз, фраза отключается. Когда все фразы отключатся диалог заканчивается. Реакция -- это строка кода на lua который выполнится после отключения фразы. Например:
{{{
<code>
food = obj {
nam = 'еда',
inv = function (s)
@ -548,7 +548,7 @@ povardlg = dlg {
[4] = phr('Мне что-нибудь легонькое, у меня язва...', 'Овсянка!', [[pon(4); return gotfood(4);]]),
},
};
}}}
</code>
В данном примере, игрок выбирает еду. Получае ее (запомнив выбор в переменной food._num) и возвращается обратно (в ту сцену откуда попал в диалог).
В реакции может быть любой lua код, но в STEAD определены наиболее часто используемые функции:
@ -558,7 +558,7 @@ poff(n...) -- выключить фразы диалога с номерами n
prem(n...) -- удалить (заблокировать) фразы диалога с номерами n... (удаление означает невозможность включения фраз. pon(n..) не приведет к включению фраз).
Переход в диалог осуществляется как переход на сцену:
{{{
<code>
povar = obj {
nam = 'повар',
dsc = 'Я вижу {повара}.',
@ -566,11 +566,11 @@ povar = obj {
return goto('povardlg');
end,
};
}}}
</code>
Вы можете переходить из одного диалога в другой диалог -- организовывая иерархические диалоги.
Также, вы можете прятать некоторые фразы при инициализации диалога и показывать их при некоторых условиях.
{{{
<code>
facectrl = dlg {
nam = 'фэйсконтроль',
dsc = 'Я вижу перед собой неприятное лицо полного охранника.',
@ -590,16 +590,16 @@ facectrl = dlg {
s:pon(1);
end,
};
}}}
</code>
`_phr` -- создает выключенную фразу, которую можно включить. Данный пример показывает также возможность использования методов pon, poff, prem для диалога (см. exit).
Вы можете включать/выключать фразы не только текущего, но и произвольного диалога, с помощью методов объекта диалог pon/poff. Например: shopman:pon(5);
== 15. Облегченные объекты ==
===== 15. Облегченные объекты =====
Иногда, сцену нужно наполнить декорациями, которые обладают ограниченной функциональностью, но делают игру разнообразней. Для этого можно использовать облегченный объект. Например:
{{{
<code>
sside = room {
nam = 'южная сторона',
dsc = [[Я нахожусь у южной стены здания института. ]],
@ -615,7 +615,7 @@ sside = room {
obj = { vobj(1, "подъезд", "У восточного угла находится небольшой {подъезд}."),
vobj(2, "люди", "Время от времени дверь подъезда хлопает впуская и выпуская {людей}.")},
};
}}}
</code>
Как видим, vobj позволяет сделать легкую версию статического объекта, с которым тем не менее можно взаимодействовать (за счет определения обработчика act в сцене и анализа ключа объекта). vobj также вызывает метод used, при этом в качестве третьего параметра передается объект, воздействующий на виртуальный объект.
Синтаксис vobj: vobj(ключ, имя, описатель); где ключ -- это цифра, которая будет передана обработчикам act/used сцены как второй параметр.
@ -623,29 +623,29 @@ sside = room {
Существует модификация объекта vobj -- vway. vway реализует ссылку.
Синтаксис vway: vway(имя, описатель, сцена назначения); например:
{{{
<code>
obj = { vway("дальше", "Нажмите {здесь}.", 'nextroom') }
}}}
</code>
Вы можете динамически заполнять сцену объектами vobj или vway с помощью методов add и del. Например:
{{{
<code>
home.objs:add(vway("next", "{Дальше}.", 'next_room');
-- some code here
home.objs:del("next");
}}}
</code>
Определена также упрощенная сцена vroom.
Синтаксис: vroom(имя перехода, сцена назначения). Например:
{{{
<code>
home.objs:add(vroom("идти на запад", 'mountains');
}}}
</code>
== 16. Динамические события ==
===== 16. Динамические события =====
Вы можете определять обработчики, которые выполняются каждый раз, когда время игры увеличивается на 1. Например:
{{{
<code>
mycat = obj {
nam = 'Барсик',
lf = {
@ -674,28 +674,28 @@ profdlg2 = dlg {
'Я выхватываю Барсика из руки Белина и засовываю себе за пазуху.',
[[inv():add('mycat'); lifeon('mycat')]]),
....
}}}
</code>
Любой объект или сцена могут иметь свой обработчик life, который вызывается каждый раз при смене текущего времени игры, если объект или сцена были добавлены в список живых объектов с помощью lifeon. Не забывайте удалять живые объекты из списка с помощью lifeoff, когда они больше не нужны. Это можно сделать, например, в обработчике exit, или любым другим способом.
life метод может возвращать текст события, который печатается после описания сцены.
Начиная с версии 0.9.1 вы можете вернуть из обработчика life второй код возврата, важность. (true или false). Например:
{{{
<code>
return 'В комнату вошел охранник.', true
}}}
</code>
При этом текст события будет выведен до описания объектов.
== 17. Графика и музыка ==
===== 17. Графика и музыка =====
Графический интерпретатор анализирует атрибут сцены pic, и воспринимает его как путь к картинке, например:
{{{
<code>
home = room {
pic = 'gfx/home.png',
nam = 'дома',
dsc = 'Я у себя дома',
};
}}}
</code>
Конечно, pic может быть функцией, расширяя возможности разработчика.
Если в текущей сцене не определен атрибут pic, то берется атрибут game.pic. Если не определен и он, то картинка не отображается.
@ -703,22 +703,22 @@ home = room {
Начиная с версии 0.9.2 вы можете использовать в качестве картинок анимированные gif файлы.
Начиная с версии 0.9.2 вы можете встраивать графические изображения в текст или в инвентарь с помощью функции img. Например:
{{{
<code>
knife = obj {
nam = 'Нож'..img('img/knife.png'),
}
}}}
</code>
Начиная с версии 1.0.0 интерпретатор может обрабатывать составные картинки, например:
{{{
<code>
pic = 'gfx/mycat.png;gfx/milk.png@120,25;gfx/fish.png@32,32'
}}}
</code>
Интерпретатор проигрывает в цикле текущую музыку, которая задается с помощью функции:
set_music(имя музыкального файла).
Например:
{{{
<code>
street = room {
pic = 'gfx/street.png',
enter = function()
@ -727,7 +727,7 @@ street = room {
nam = 'на улице',
dsc = 'На улице идет дождь.',
};
}}}
</code>
get_music() возвращает текущее имя трека.
@ -739,37 +739,41 @@ get_music() возвращает текущее имя трека.
is_music() позволяет узнать, проигрывается ли музыка. (с версии 1.0.0)
== 18. Полезные советы ==
=== Форматирование ===
===== 18. Полезные советы =====
==== Форматирование ====
Вы можете делать простое форматирование текста с помощью функций:
txtc() - разместить по центру;
txtr() - разместить справа;
txtl() - разместить слева;
* txtc() - разместить по центру;
* txtr() - разместить справа;
* txtl() - разместить слева;
Например:
{{{
<code>
main = room {
nam = 'Intro',
dsc = txtc('Добро пожаловать!'),
}
}}}
</code>
Вы также можете менять оформление текста с помощью комбинаций функций:
txtb() - жирный;
txtem() - курсив;
txtu() - подчеркнутый;
* txtb() - жирный;
* txtem() - курсив;
* txtu() - подчеркнутый;
Начиная с версии 1.1.0 вы также можете создавать неразрываемые строки с помощью: txtnb();
Например:
{{{
<code>
main = room {
nam = 'Intro',
dsc = 'Вы находитесь в комнате '..txtb('main')..'.',
}
}}}
=== Меню ===
</code>
==== Меню ====
Вы можете делать меню в области инвентаря, определяя объекты с типом menu. При этом, обработчик меню будет вызван после одного клика мыши. Если обработчик не возвращает текст, то состояние игры не изменяется. Например, реализация кармана:
{{{
<code>
pocket = menu {
State = false,
nam = function(s)
@ -807,12 +811,12 @@ pocket:gen();
main = room {
nam = 'test',
};
}}}
</code>
=== Статус игрока ===
==== Статус игрока ====
Код движка расположен в файле stead.lua. В своей игре вы можете переопределять любую функцию или объект lua, добиваясь того, что нужно для вашего творческого замысла. Кроме того, полезно знать особенности работы движка. Например, ниже представлена реализация статуса игрока в виде текста, который появляется в инвентаре, но не может быть выбран.
{{{
<code>
pl.Life = 10;
pl.Power = 10;
@ -821,76 +825,76 @@ status = obj {
};
inv():add('status');
status.object_type = false
}}}
</code>
Начиная с версии 0.9.1 вы можете использовать конструктор stat для создания статуса.
=== goto из обработчика exit ==
==== goto из обработчика exit ===
Если вы выполните goto из обработчика exit, то получите переполнение стека, так как goto снова и снова будет вызывать метод exit. Вы можете избавиться от этого, если вставите проверку, разрушающую рекурсию. Например:
{{{
<code>
exit = function(s, t)
if t == 'dialog' then return; end
return goto('dialog');
}}}
</code>
Начиная с версии 0.9.1 движок сам разрывает рекурсию.
Вы можете также делать goto из обработчиков enter.
=== Динамически создаваемые ссылки. ==
==== Динамически создаваемые ссылки. ====
Динамически создаваемые ссылки могут быть реализованы разным способом. Ниже приводится пример, основанный на использовании объектов vway. Для добавления ссылки можно использовать запись:
{{{
<code>
home.obj:add(vway('Дорога', 'Я заметил {дорогу}, ведущую в лес...', 'forest'));
}}}
</code>
Для удаления ссылки можно использовать метод del.
{{{
<code>
home.obj:del('Дорога');
}}}
</code>
Для определения наличия ссылки в сцене -- метод srch.
{{{
<code>
if not home.obj:srch('Дорога') then
home.obj:add(vway('Дорога', 'Я заметил {дорогу}, ведущую в лес...', 'forest'));
end
}}}
</code>
Динамические ссылки удобно создавать в обработчике enter, или по мере необходимости в любом месте кода игры. Если ссылки создаются в текущей сцене, то последний пример можно упростить:
{{{
<code>
if not seen('Дорога') then
objs():add(vway('Дорога', 'Я заметил {дорогу}, ведущую в лес...', 'forest'));
end
}}}
</code>
Кроме того, вы можете просто включать и выключать ссылки с помощью enable(), disable(), например:
{{{
<code>
objs()[1]:disable();
}}}
</code>
Вы можете создавать выключенные vobj и vway следующим образом:
{{{
<code>
obj = {vway('Дорога', 'Я заметил {дорогу}, ведущую в лес...', 'forest'):disable()},
}}}
</code>
И затем включать их по индексу в массиве obj:
{{{
<code>
objs()[1]:enable();
}}}
</code>
=== Кодирование исходного кода игры (начиная с версии 0.9.3) ===
==== Кодирование исходного кода игры (начиная с версии 0.9.3) ====
Если вы не хотите показывать исходный код своих игр, вы можете закодировать исходный код с помощью sdl-instead -encode <путь к файлу> [выходной путь] и использовать его с помощью lua функции doencfile. При этом главный файл main.lua необходимо оставлять текстовым. Таким образом схема выглядит следующим образом (game -- закодированный game.lua):
main.lua
{{{
<code>
-- $Name: Моя закрытая игра!$
doencfile("game");
}}}
</code>
ЗАМЕЧАНИЕ:
Не используйте компиляцию игр с помощью luac, так как luac создает платформозависимый код!
Однако, компиляция игр может быть использована для поиска ошибок в коде.
=== Переключение между игроками ===
==== Переключение между игроками ====
Вы можете создать игру с несколькими персонажами и время от времени переключаться между ними (см. switch_pl). Но вы можете также использовать этот трюк для того, что бы иметь возможность переключаться между разными типами инвентаря.
=== Использование первого параметра обработчика ===
==== Использование первого параметра обработчика ====
Пример кода.
{{{
<code>
knife = obj {
nam = 'камень',
dsc = 'На краю лежит {камень}.',
@ -898,20 +902,20 @@ knife = obj {
objs():del('knife');
return 'Я толкнул камень, он сорвался и улетел вниз...';
end
}}}
</code>
Обработчик act мог бы выглядеть проще:
{{{
<code>
act = function(s)
objs():del(s);
return 'Я толкнул камень, он сорвался и улетел вниз...';
end
}}}
=== Использование set_music ===
</code>
==== Использование set_music ====
Вы можете использовать set_music для проигрывания звуков, задавая второй параметр -- счетчик циклов проигрывания звукового файла.
Вы можете написать для игры свой проигрыватель музыки, создав его на основе живого объекта, например:
{{{
<code>
-- играет треки в случайном порядке, начиная со 2-го
tracks = {"mus/astro2.mod", "mus/aws_chas.xm", "mus/dmageofd.xm", "mus/doomsday.s3m"}
mplayer = obj {
@ -931,11 +935,11 @@ mplayer = obj {
end,
};
lifeon('mplayer');
}}}
</code>
Вы можете использовать функции get_music_loop и get_music, для того, чтобы запоминать прошлую мелодию, и потом восстанавливать ее, например:
{{{
<code>
function save_music(s)
s.OldMusic = get_music();
s.OldMusicLoop = get_music_loop();
@ -954,13 +958,13 @@ exit = function(s)
end,
-- ....
}}}
</code>
Начиная с версии 0.8.5 функции save_music и restore_music уже присутствуют в библиотеке.
=== Живые объекты ===
==== Живые объекты ====
Если вашему герою нужен друг, одним из способов может стать метод life этого персонажа, который всегда переносит объект в локацию игрока:
{{{
<code>
horse = obj {
nam = 'лошадь',
dsc = 'Рядом со мной стоит {лошадь}.',
@ -972,22 +976,79 @@ horse = obj {
end,
};
lifeon('horse');
}}}
</code>
==== Таймер ====
Начиная с версии 1.1.0 в instead появлилась возможность использовать таймер. (Только в графической версии интерпретатора.)
=== Отладка ===
Таймер программируется с помощью объекта timer.
* timer:set(ms) -- задать интервал таймера в ms
* timer:del() -- удалить таймер
* timer.callback(s) -- функция-обработчик таймера, которая вызывается через заданный диапазон времени
Функция таймера может возвратить комнаду интерфейса stead, которую нужно выполнить после того, как движок выполнит обработчик. Например:
<code>
timer.callback = function(s)
main._time = main._time + 1;
return "look";
end
timer:set(100);
main = room {
_time = 1,
force_dsc = true,
nam = 'Таймер',
dsc = function(s)
return 'Демонстрация: '..tostring(s._time);
end
};
</code>
==== Клавиатура ====
Начиная с версии 1.1.0 в instead появлилась возможность анализировать ввод с клавиатуры. (Только в графической версии интерпретатора.) Для этого используется объект input.
input.key(s, pressed, key) -- обработчик клавиатуры; pressed -- нажатие или отжатие. key -- символьное имя клавиши;
Обработчик может вернуть команду интерфейса stead, в этом случае клавиша не будет обработана интерпретатором.
Например:
<code>
input.key = function(s, pr, key)
if not pr or key == "escape"then
return
elseif key == 'space' then
key = ' '
elseif key == 'return' then
key = '^';
end
if key:len() > 1 then return end
main._txt = main._txt:gsub('_$','');
main._txt = main._txt..key..'_';
return "look";
end
main = room {
_txt = '_',
force_dsc = true,
nam = 'Клавиатура',
dsc = function(s)
return 'Демонстрация: '..tostring(s._txt);
end
};
</code>
==== Отладка ====
Для того, чтобы во время ошибки увидеть стек вызовов функций lua, вы можете запустить sdl-instead с параметром -debug. При этом в windows версии интерпретатора будет создана консоль отладки.
Вы можете отлаживать свою игру вообще без instead. Например, вы можете создать следующий файл game.lua:
{{{
<code>
dofile("/usr/share/games/stead/stead.lua"); -- путь к stead.lua
dofile("main.lua"); -- ваша игра
game:ini();
iface:shell();
}}}
</code>
И запустите игру в lua: lua game.lua.
При этом игра будет работать в примитивном shell окружении. Полезные команды: ls, go, act, use....
== 19. Темы для sdl-instead ==
===== 19. Темы для sdl-instead =====
Графический интерпретатор поддерживает механизм тем. Тема представляет из себя каталог, с файлом theme.ini внутри.
@ -1090,16 +1151,16 @@ snd.click = путь к звуковому файлу щелчка (строка
include = имя темы (последний компонент в пути каталога) (строка)
Кроме того, заголовок темы может включать в себя комментарии с тегами. На данный момент существует только один тег: $Name:, содержащий UTF-8 строку с именем темы. Например:
{{{
<code>
; $Name:Новая тема$
; модификация темы book
include = book
scr.gfx.h = 500
}}}
</code>
Интерпретатор выполняет поиск тем в каталоге themes. Unix версия кроме этого каталога, просматривает также каталог ~/.instead/themes/
Windows версия (>=0.8.7): Documents and Settings/USER/Local Settings/Application Data/instead/themes
TODO
Полный список объектов и методов.
Полный список объектов и методов.