This commit is contained in:
p.kosyh 2010-05-19 17:38:09 +00:00
parent 658c28538a
commit 53701da01c
2 changed files with 519 additions and 511 deletions

View file

@ -157,27 +157,27 @@ If the attribute or handler is laid out as a function, then the first argument o
If you know lua, you can simplify: If you know lua, you can simplify:
<code> <code>
function sw(v, a, b) function sw(v, a, b)
if v then if v then
return a; return a;
end end
return b return b
end end
aple = obj { aple = obj {
nam = function(s) nam = function(s)
return sw(not s._seen, 'unknown','apple'); return sw(not s._seen, 'unknown','apple');
end, end,
dsc = function(s) dsc = function(s)
return sw(not s._seen,'There is {something} on the table.', 'There is an {apple} on the table.'); return sw(not s._seen,'There is {something} on the table.', 'There is an {apple} on the table.');
end, end,
act = function(s) act = function(s)
if s._seen then if s._seen then
return 'It\'s an apple!'; return 'It\'s an apple!';
else else
s._seen = true; s._seen = true;
return 'Hm... But it\'s an apple!'; return 'Hm... But it\'s an apple!';
end end
end, end,
}; };
</code> </code>
And then always use “sw” or some other auxiliary function. And then always use “sw” or some other auxiliary function.
@ -189,7 +189,7 @@ Warning!!! The variables outside any of the following object types: room, object
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: 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> <code>
function isForSave(k) function isForSave(k)
return string.find(k, '_') == 1 or string.match(k,'^%u') return string.find(k, '_') == 1 or string.match(k,'^%u')
end end
</code> </code>
@ -198,20 +198,20 @@ There are two extra parameters for isForSave() (for instead versions > 1.0.0). v
Sometimes we need a handler that would do something without showing any description, e.g.: Sometimes we need a handler that would do something without showing any description, e.g.:
<code> <code>
button = obj { button = obj {
nam = "button", nam = "button",
dsc = "There is a big red {button} on the room wall.", dsc = "There is a big red {button} on the room wall.",
act = function (s) act = function (s)
here()._dynamic_dsc = [[The room transformed after I pressed the button. here()._dynamic_dsc = [[The room transformed after I pressed the button.
The book-case disappeared along with the table and the chest, and a strange The book-case disappeared along with the table and the chest, and a strange
looking device took its place.]]; looking device took its place.]];
return true; return true;
end, end,
} }
r12 = room { r12 = room {
nam = 'room', nam = 'room',
_dynamic_dsc = 'I am in the room.', _dynamic_dsc = 'I am in the room.',
dsc = function (s) return s._dynamic_dsc end, dsc = function (s) return s._dynamic_dsc end,
obj = {'button'} obj = {'button'}
} }
</code> </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. 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.
@ -271,7 +271,7 @@ room2 = room {
}; };
</code> </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: “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> <code>
room2 = room { room2 = room {
enter = function(s, f) enter = function(s, f)
@ -325,7 +325,7 @@ knife = obj {
inv = 'Sharp!', inv = 'Sharp!',
tak = 'I took the knife!', tak = 'I took the knife!',
use = function(s, w) use = function(s, w)
if w ~= 'tabl' if w ~= 'tabl' then
return 'I don\'t want to cut this.', false return 'I don\'t want to cut this.', false
else else
return 'You incise your initials on the table.'; return 'You incise your initials on the table.';
@ -340,9 +340,9 @@ You can use the knife only on the table.
In STEAD the player is represented by the object “pl”. The object type is “player”. In the engine it's created thie way: In STEAD the player is represented by the object “pl”. The object type is “player”. In the engine it's created thie way:
<code> <code>
pl = player { pl = player {
nam = "Incognito", nam = "Incognito",
where = 'main', where = 'main',
obj = { } obj = { }
}; };
</code> </code>
The “obj” attribute represents the player's inventory. The “obj” attribute represents the player's inventory.
@ -352,17 +352,17 @@ The “obj” attribute represents the player's inventory.
The game is also represented by the object “game” of type “game”. In the engine it is defined this way: The game is also represented by the object “game” of type “game”. In the engine it is defined this way:
<code> <code>
game = game { game = game {
nam = "INSTEAD -- Simple Text Adventure interpreter v"..version.." '2009 by Peter Kosyh", nam = "INSTEAD -- Simple Text Adventure interpreter v"..version.." '2009 by Peter Kosyh",
dsc = [[ dsc = [[
Commands:^ Commands:^
look(or just enter), act <on what> (or just what), use <what> [on what], go <where>,^ look(or just enter), act <on what> (or just what), use <what> [on what], go <where>,^
back, inv, way, obj, quit, save <fname>, load <fname>.]], back, inv, way, obj, quit, save <fname>, load <fname>.]],
pl ='pl', pl ='pl',
showlast = true, showlast = true,
}; };
</code> </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: 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:
<code> game.codepage="UTF-8"; </code> <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. The support of arbitrary encodings is present in every UNIX version of the interpreter and in windows versions from 0.7.7.
@ -469,11 +469,11 @@ end
move(o, w) — moves an object from the current scene to another: move(o, w) — moves an object from the current scene to another:
<code> move('mycat','inmycar');</code> <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”. 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”.
<code> move('mycat','inmycar', 'forest'); </code> <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. From version 0.8 there is a “movef” function similar to “move”, but adding the object to the start of the list.
@ -509,11 +509,11 @@ change_pl(p) — switch to another player (with one's own inventory and position
<code> <code>
mycar = obj { mycar = obj {
nam = 'my car', nam = 'my car',
dsc = 'In front of the cabin there is my old Toyota {pickup}.', dsc = 'In front of the cabin there is my old Toyota {pickup}.',
act = function(s) act = function(s)
return goto('inmycar'); return goto('inmycar');
end end
}; };
</code> </code>
@ -530,42 +530,42 @@ par(...) — returns the string, concatenating argument strings split by the fir
Dialogs are scenes with phrase objects. The simplest dialog may look like this: Dialogs are scenes with phrase objects. The simplest dialog may look like this:
<code> <code>
povardlg = dlg { povardlg = dlg {
nam = 'in the kitchen', nam = 'in the kitchen',
dsc = 'I see a fat face of a lady cook wearing a white hat with a tired look...', dsc = 'I see a fat face of a lady cook wearing a white hat with a tired look...',
obj = { obj = {
[1] = phr('“Those green, please... Yeah, and beans too!”', '“Enjoy!”'), [1] = phr('“Those green, please... Yeah, and beans too!”', '“Enjoy!”'),
[2] = phr('“Fried potato with lard, please!”', '“Bon appetit!”'), [2] = phr('“Fried potato with lard, please!”', '“Bon appetit!”'),
[3] = phr('“Two helpings of garlic sooup!!!”', '“Good choice!”'), [3] = phr('“Two helpings of garlic sooup!!!”', '“Good choice!”'),
[4] = phr('“Something light, please, I've got an ulcer...”', '“Oatmeal!”'), [4] = phr('“Something light, please, I've got an ulcer...”', '“Oatmeal!”'),
}, },
}; };
</code> </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.: “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> <code>
food = obj { food = obj {
nam = 'food', nam = 'food',
inv = function (s) inv = function (s)
inv():del('food'); inv():del('food');
return 'I eat.'; return 'I eat.';
end end
}; };
gotfood = function(w) gotfood = function(w)
inv():add('food'); inv():add('food');
food._num = w; food._num = w;
return back(); return back();
end end
povardlg = dlg { povardlg = dlg {
nam = 'in the kitchen', nam = 'in the kitchen',
dsc = 'I see a fat face of a lady cook wearing a white hat with a tired look...', dsc = 'I see a fat face of a lady cook wearing a white hat with a tired look...',
obj = { obj = {
[1] = phr('“Those green, please... Yeah, and beans too!”', '“Enjoy!”', [[pon(1); return gotfood(1);]]), [1] = phr('“Those green, please... Yeah, and beans too!”', '“Enjoy!”', [[pon(1); return gotfood(1);]]),
[2] = phr('“Fried potato with lard, please!”', '“Bon appetit!”', [[pon(2); return gotfood(2);]]), [2] = phr('“Fried potato with lard, please!”', '“Bon appetit!”', [[pon(2); return gotfood(2);]]),
[3] = phr('“Two helpings of garlic sooup!!!”', '“Good choice!”', [[pon(3);return gotfood(3);]]), [3] = phr('“Two helpings of garlic sooup!!!”', '“Good choice!”', [[pon(3);return gotfood(3);]]),
[4] = phr('“Something light, please, I've got an ulcer...”', '“Oatmeal!”', [[pon(4); return gotfood(4);]]), [4] = phr('“Something light, please, I've got an ulcer...”', '“Oatmeal!”', [[pon(4); return gotfood(4);]]),
}, },
}; };
</code> </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. 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.
@ -591,23 +591,23 @@ 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. You can also hide some phrases when initializing the dialog and show them under certain conditions.
<code> <code>
facectrl = dlg { facectrl = dlg {
nam = 'фэйсконтроль', nam = 'facecontrol',
dsc = 'I see an unpleasant face of a fat guard.', dsc = 'I see an unpleasant face of a fat guard.',
obj = { obj = {
[1] = phr('“I came to the Belin's lecture...”', [1] = phr('“I came to the Belin's lecture...”',
'“I do not know who you are,” he smiles, “but I have orders to let in only decent people.”', '“I do not know who you are,” he smiles, “but I have orders to let in only decent people.”',
[[pon(2);]]), [[pon(2);]]),
[2] = _phr('“I\'ve got an invitation!”', [2] = _phr('“I\'ve got an invitation!”',
'“And I don\'t care! Look at yourself in a mirror!!! You\'ve come to listen to Belin himself — the right hand of...” he made a respectful pause. “So get lost...”', [[pon(3,4)]]), '“And I don\'t care! Look at yourself in a mirror!!! You\'ve come to listen to Belin himself — the right hand of...” he made a respectful pause. “So get lost...”', [[pon(3,4)]]),
[3] = _phr(' “I\'m gonna kick your ass!”', '“I\'ve had enough...” Strong arms push me out to the corridor...', [3] = _phr(' “I\'m gonna kick your ass!”', '“I\'ve had enough...” Strong arms push me out to the corridor...',
[[poff(4)]]), [[poff(4)]]),
[4] = _phr('“You, boar! I\'ve told you, I\'ve got an invitation!”', [4] = _phr('“You, boar! I\'ve told you, I\'ve got an invitation!”',
'“Whaaat?” The guard\'s eyes start getting bloodshot... A powerful kick sends me out to the corridor...', '“Whaaat?” The guard\'s eyes start getting bloodshot... A powerful kick sends me out to the corridor...',
[[poff(3)]]), [[poff(3)]]),
}, },
exit = function(s,w) exit = function(s,w)
s:pon(1); s:pon(1);
end, end,
}; };
</code> </code>
@ -620,19 +620,19 @@ You can enable/disable phrases not only of the current put of any arbitrary dial
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: 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> <code>
sside = room { sside = room {
nam = 'southern side', nam = 'southern side',
dsc = [[I am near the southern wall of an institute building. ]], dsc = [[I am near the southern wall of an institute building. ]],
act = function(s, w) act = function(s, w)
if w == 1 then if w == 1 then
ways():add('stolcorridor'); ways():add('stolcorridor');
return "I walked to the porch. The sign on the door read 'Canteen'. Hm... should I get in?"; return "I walked to the porch. The sign on the door read 'Canteen'. Hm... should I get in?";
end end
if w == 2 then if w == 2 then
return 'The ones going out look happier...'; return 'The ones going out look happier...';
end end
end, end,
obj = { vobj(1, "porch", "There is a small {porch} by the eastern corner."), 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..")}, vobj(2, "people", "From time to time the porch door slams letting {people} in and out..")},
}; };
</code> </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. 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.
@ -649,7 +649,7 @@ There is a modification of “vobj” object — “vway”. It creates a refere
You can dynamically fill the scene with “vobj” and “vway” objects. Use methods “add” and “del”. For example: You can dynamically fill the scene with “vobj” and “vway” objects. Use methods “add” and “del”. For example:
<code> <code>
home.objs:add(vway("next", "{Next}.", 'next_room'); home.objs:add(vway("next", "{Next}.", 'next_room'));
-- some code here -- some code here
home.objs:del("next"); home.objs:del("next");
</code> </code>
@ -666,32 +666,32 @@ Syntax: vroom(passage name, destination scene). For example:
You can define handlers, that would execute every time when the game time increments by 1. E.g.: You can define handlers, that would execute every time when the game time increments by 1. E.g.:
<code> <code>
mycat = obj { mycat = obj {
nam = 'Barsik', nam = 'Barsik',
lf = { lf = {
[1] = 'Barsik is moving in my bosom.', [1] = 'Barsik is moving in my bosom.',
[2] = 'Barsik peers out of my bosom.', [2] = 'Barsik peers out of my bosom.',
[3] = 'Barsik purrs in my bosom.', [3] = 'Barsik purrs in my bosom.',
[4] = 'Barsik shivers in my bosom.', [4] = 'Barsik shivers in my bosom.',
[5] = 'I feel Barsik's warmth in my bosom.', [5] = 'I feel Barsik's warmth in my bosom.',
[6] = 'Barsik leans out of my bosom and looks around.', [6] = 'Barsik leans out of my bosom and looks around.',
}, },
life = function(s) life = function(s)
local r = rnd(6); local r = rnd(6);
if r > 2 then if r > 2 then
return; return;
end end
r = rnd(6); r = rnd(6);
return s.lf[r]; return s.lf[r];
end, end,
.... ....
profdlg2 = dlg { profdlg2 = dlg {
nam = 'Belin', nam = 'Belin',
dsc = 'Belin is pale. He absently looks at the shotgun.', dsc = 'Belin is pale. He absently looks at the shotgun.',
obj = { obj = {
[1] = phr('“I came for my cat.”', [1] = phr('“I came for my cat.”',
'I snatch Barsik from Belin's hand and put in my bosom.', 'I snatch Barsik from Belin's hand and put in my bosom.',
[[inv():add('mycat'); lifeon('mycat')]]), [[inv():add('mycat'); lifeon('mycat')]]),
.... ....
</code> </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. 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.
@ -709,9 +709,9 @@ Graphic interpreter analyzes the scene “pic” attribute and treats it as a pa
<code> <code>
home = room { home = room {
pic = 'gfx/home.png', pic = 'gfx/home.png',
nam = 'at home', nam = 'at home',
dsc = 'I am at home', dsc = 'I am at home',
}; };
</code> </code>
@ -724,7 +724,7 @@ From version 0.9.2 graphics can be embedded everywhere in text or inventory with
<code> <code>
knife = obj { knife = obj {
nam = 'Knife'..img('img/knife.png'), nam = 'Knife'..img('img/knife.png'),
} }
</code> </code>
@ -733,12 +733,12 @@ The interpreter cycles the current music defined by the function ”set_music(mu
For example: For example:
<code> <code>
street = room { street = room {
pic = 'gfx/street.png', pic = 'gfx/street.png',
enter = function() enter = function()
set_music('mus/rain.ogg'); set_music('mus/rain.ogg');
end, end,
nam = 'on the street', nam = 'on the street',
dsc = 'It is raining outside.', dsc = 'It is raining outside.',
}; };
</code> </code>
@ -771,8 +771,8 @@ txtl() - left align;
For example: For example:
<code> <code>
main = room { main = room {
nam = 'Intro', nam = 'Intro',
dsc = txtc('Welcome!'), dsc = txtc('Welcome!'),
} }
</code> </code>
@ -785,8 +785,8 @@ txtu() - underline;
For example: For example:
<code> <code>
main = room { main = room {
nam = 'Intro', nam = 'Intro',
dsc = 'You are in the room: '..txtb('main')..'.', dsc = 'You are in the room: '..txtb('main')..'.',
} }
</code> </code>
@ -795,8 +795,8 @@ Since the version 1.1.0 you can create unwrapped strings by using txtnb();
For example: For example:
<code> <code>
main = room { main = room {
nam = 'Intro', nam = 'Intro',
dsc = 'You are in the room '..txtb('main')..'.', dsc = 'You are in the room '..txtb('main')..'.',
} }
</code> </code>
@ -804,33 +804,33 @@ main = room {
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: 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> <code>
pocket = menu { pocket = menu {
State = false, State = false,
nam = function(s) nam = function(s)
if s.State then if s.State then
return txtu('pocket'); return txtu('pocket');
end end
return 'pocket'; return 'pocket';
end, end,
gen = function(s) gen = function(s)
if s.State then if s.State then
s:enable_all(); s:enable_all();
else else
s:disable_all(); s:disable_all();
end end
end, end,
menu = function(s) menu = function(s)
if s.State then if s.State then
s.State = false; s.State = false;
else else
s.State = true; s.State = true;
end end
s:gen(); s:gen();
end, end,
}; };
knife = obj { knife = obj {
nam = 'knife', nam = 'knife',
inv = 'This is knife', inv = 'This is knife',
}; };
inv():add(pocket); inv():add(pocket);
@ -838,7 +838,7 @@ put(knife, pocket);
pocket:gen(); pocket:gen();
main = room { main = room {
nam = 'test', nam = 'test',
}; };
</code> </code>
==== Player status ==== ==== Player status ====
@ -849,9 +849,9 @@ pl.Life = 10;
pl.Power = 10; pl.Power = 10;
status = obj { status = obj {
nam = function(s) nam = function(s)
return 'Life: '..pl.Life..',Power: '..pl.Power return 'Life: '..pl.Life..',Power: '..pl.Power
end end
}; };
inv():add('status'); inv():add('status');
status.object_type = false status.object_type = false
@ -863,9 +863,9 @@ pl.Life = 10;
pl.Power = 10; pl.Power = 10;
status = stat { status = stat {
nam = function(s) nam = function(s)
return 'Life: '..pl.Life..',Power: '..pl.Power return 'Life: '..pl.Life..',Power: '..pl.Power
end end
}; };
inv():add('status'); inv():add('status');
</code> </code>
@ -875,8 +875,9 @@ inv():add('status');
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: 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> <code>
exit = function(s, t) exit = function(s, t)
if t == 'dialog' then return; end if t == 'dialog' then return; end
return goto('dialog'); return goto('dialog');
end
</code> </code>
From version 0.9.1 this is done by stead engine. From version 0.9.1 this is done by stead engine.
@ -895,27 +896,27 @@ home.obj:del('Road');
The “srch” method can check if the reference is present in the scene. The “srch” method can check if the reference is present in the scene.
<code> <code>
if not home.obj:srch('Road') then if not home.obj:srch('Road') then
home.obj:add(vway('Road', 'I noticed a {road} going into the forest...', 'forest')); home.obj:add(vway('Road', 'I noticed a {road} going into the forest...', 'forest'));
end end
</code> </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: 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> <code>
if not seen('Road') then if not seen('Road') then
objs():add(vway('Road', 'I noticed a {road} going into the forest...', 'forest')); objs():add(vway('Road', 'I noticed a {road} going into the forest...', 'forest'));
end end
</code> </code>
Or you can just enable and disable references with “enable()” and “disable()”, for example: Or you can just enable and disable references with “enable()” and “disable()”, for example:
<code> <code>
objs()[1]:disable(); objs()[1]:disable();
</code> </code>
Creating disabled “vobj” and “vway”: Creating disabled “vobj” and “vway”:
<code> <code>
obj = {vway('Road', 'I noticed a {road} going into the forest...', 'forest'):disable()}, obj = {vway('Road', 'I noticed a {road} going into the forest...', 'forest'):disable()},
</code> </code>
And then enabling them by their index in the “obj” array: And then enabling them by their index in the “obj” array:
<code> <code>
objs()[1]:enable(); objs()[1]:enable();
</code> </code>
==== Encoding game sources (from version 0.9.3) ==== ==== Encoding game sources (from version 0.9.3) ====
@ -937,21 +938,21 @@ You can create a game with several characters and switch between them from time
==== Using the first parameter of a handler ==== ==== Using the first parameter of a handler ====
Code example. Code example.
<code> <code>
knife = obj { stone = obj {
nam = 'stone', nam = 'stone',
dsc = 'There is a {stone} at the edge.', dsc = 'There is a {stone} at the edge.',
act = function() act = function()
objs():del('knife'); objs():del('stone');
return 'I pushed the stone, it fell and flew down...'; return 'I pushed the stone, it fell and flew down...';
end end
</code> </code>
The “act” handler could look simpler: The “act” handler could look simpler:
<code> <code>
act = function(s) act = function(s)
objs():del(s); objs():del(s);
return 'I pushed the stone, it fell and flew down...'; return 'I pushed the stone, it fell and flew down...';
end end
</code> </code>
==== Using “set_music” ==== ==== Using “set_music” ====
@ -962,20 +963,20 @@ You can write your own music player, creating it from a live object, e.g:
-- plays tracks in random order, starting from 2-nd -- plays tracks in random order, starting from 2-nd
tracks = {"mus/astro2.mod", "mus/aws_chas.xm", "mus/dmageofd.xm", "mus/doomsday.s3m"} tracks = {"mus/astro2.mod", "mus/aws_chas.xm", "mus/dmageofd.xm", "mus/doomsday.s3m"}
mplayer = obj { mplayer = obj {
nam = 'плеер', nam = 'media player',
life = function(s) life = function(s)
local n = get_music(); local n = get_music();
local v = get_music_loop(); local v = get_music_loop();
if not n or not v then if not n or not v then
set_music(tracks[2], 1); set_music(tracks[2], 1);
elseif v == -1 then elseif v == -1 then
local n = get_music(); local n = get_music();
while get_music() == n do while get_music() == n do
n = tracks[rnd(4)] n = tracks[rnd(4)]
end end
set_music(n, 1); set_music(n, 1);
end end
end, end,
}; };
lifeon('mplayer'); lifeon('mplayer');
</code> </code>
@ -984,20 +985,20 @@ You can use “get_music_loop” and “get_music” functions to remember the l
<code> <code>
function save_music(s) function save_music(s)
s.OldMusic = get_music(); s.OldMusic = get_music();
s.OldMusicLoop = get_music_loop(); s.OldMusicLoop = get_music_loop();
end end
function restore_music(s) function restore_music(s)
set_music(s.OldMusic, s.OldMusicLoop); set_music(s.OldMusic, s.OldMusicLoop);
end end
-- .... -- ....
enter = function(s) enter = function(s)
save_music(s); save_music(s);
end, end,
exit = function(s) exit = function(s)
restore_music(s); restore_music(s);
end, end,
-- .... -- ....
@ -1009,14 +1010,14 @@ From version 0.8.5 functions “save_music” and “restore_music” are alread
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: 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> <code>
horse = obj { horse = obj {
nam = 'horse', nam = 'horse',
dsc = 'A {horse} is standing next to me.', dsc = 'A {horse} is standing next to me.',
life = function(s) life = function(s)
if not seen('horse') then if not seen('horse') then
move('horse', here(), s.__where); move('horse', here(), s.__where);
s.__where = pl.where; s.__where = pl.where;
end end
end, end,
}; };
lifeon('horse'); lifeon('horse');
</code> </code>
@ -1032,17 +1033,17 @@ Timer controls through the ''timer'' object.
Timer function can return a ''stead'' interface command that have to be invoked after the callback execution. For example: Timer function can return a ''stead'' interface command that have to be invoked after the callback execution. For example:
<code> <code>
timer.callback = function(s) timer.callback = function(s)
main._time = main._time + 1; main._time = main._time + 1;
return "look"; return "look";
end end
timer:set(100); timer:set(100);
main = room { main = room {
_time = 1, _time = 1,
force_dsc = true, force_dsc = true,
nam = 'Timer', nam = 'Timer',
dsc = function(s) dsc = function(s)
return 'Example: '..tostring(s._time); return 'Example: '..tostring(s._time);
end end
}; };
</code> </code>
@ -1055,26 +1056,26 @@ Handler can return a ''stead'' interface command. In this case the interpreter d
For example: For example:
<code> <code>
input.key = function(s, pr, key) input.key = function(s, pr, key)
if not pr or key == "escape"then if not pr or key == "escape"then
return return
elseif key == 'space' then elseif key == 'space' then
key = ' ' key = ' '
elseif key == 'return' then elseif key == 'return' then
key = '^'; key = '^';
end end
if key:len() > 1 then return end if key:len() > 1 then return end
main._txt = main._txt:gsub('_$',''); main._txt = main._txt:gsub('_$','');
main._txt = main._txt..key..'_'; main._txt = main._txt..key..'_';
return "look"; return "look";
end end
main = room { main = room {
_txt = '_', _txt = '_',
force_dsc = true, force_dsc = true,
nam = 'Keyboard', nam = 'Keyboard',
dsc = function(s) dsc = function(s)
return 'Example: '..tostring(s._txt); return 'Example: '..tostring(s._txt);
end end
}; };
</code> </code>
@ -1151,10 +1152,10 @@ delete(n)
''new'' treats its string argument as an object constructor. The constructor must return an object. Thus, the string argument usually contains a constructor function call. For example: ''new'' treats its string argument as an object constructor. The constructor must return an object. Thus, the string argument usually contains a constructor function call. For example:
<code> <code>
function myconstructor() function myconstructor()
local v = {} local v = {}
v.nam = 'test object', v.nam = 'test object',
v.act = 'test feedback', v.act = 'test feedback',
return obj(v); return obj(v);
end end
</code> </code>
The object created will be saved every time the game is saved. ''new()'' returns a real object; to get its name you can use ''deref'' function: The object created will be saved every time the game is saved. ''new()'' returns a real object; to get its name you can use ''deref'' function:
@ -1167,24 +1168,26 @@ delete(o_name);
Sometimes the we need to form event handler output from several parts depending on some conditions. In this case ''p()'' and ''pn()'' functions can be useful. These functions add text to the internal buffer of the handler. The content of this buffer is returned from the handler. Sometimes the we need to form event handler output from several parts depending on some conditions. In this case ''p()'' and ''pn()'' functions can be useful. These functions add text to the internal buffer of the handler. The content of this buffer is returned from the handler.
<code> <code>
dsc = function(s) dsc = function(s)
p "There is a {barrel} standing on the floor." p "There is a {barrel} standing on the floor."
if s._opened then if s._opened then
p "The barrel lid lies nearby." p "The barrel lid lies nearby."
end end
end end
</code> </code>
''pn()'' function adds line feed to the text and outputs the result to the buffer. ''p()'' does almost the same thing but adds a space instead of line feed. ''pn()'' function adds line feed to the text and outputs the result to the buffer. ''p()'' does almost the same thing but adds a space instead of line feed.
There is a function ''pr()'' in versions 1.1.6 and later, that does not add anything at end of output.
To clear the buffer you can use ''pclr()''. To return the status of the action along with the text, use ''pget()''. To clear the buffer you can use ''pclr()''. To return the status of the action along with the text, use ''pget()''.
<code> <code>
use = function(s, w) use = function(s, w)
if w == 'apple' then if w == 'apple' then
p 'I peeled the apple'; p 'I peeled the apple';
apple._peeled = true apple._peeled = true
return return
end end
p 'You cannot use it this way!' p 'You cannot use it this way!'
return pget(), false return pget(), false
end end
</code> </code>

View file

@ -153,27 +153,27 @@ apple = obj {
Зная lua, можно упростить запись: Зная lua, можно упростить запись:
<code> <code>
function sw(v, a, b) function sw(v, a, b)
if v then if v then
return a; return a;
end end
return b return b
end end
apple = obj { apple = obj {
nam = function(s) nam = function(s)
return sw(not s._seen, 'нечто','яблоко'); return sw(not s._seen, 'нечто','яблоко');
end, end,
dsc = function(s) dsc = function(s)
return sw(not s._seen,'На столе {что-то} лежит.', 'На столе лежит {яблоко}.'); return sw(not s._seen,'На столе {что-то} лежит.', 'На столе лежит {яблоко}.');
end, end,
act = function(s) act = function(s)
if s._seen then if s._seen then
return 'Это яблоко!'; return 'Это яблоко!';
else else
s._seen = true; s._seen = true;
return 'Гм... Это же яблоко!'; return 'Гм... Это же яблоко!';
end end
end, end,
}; };
</code> </code>
И в дальнейшем всегда использовать функцию sw (или какую-либо другую, вспомогательную функцию). И в дальнейшем всегда использовать функцию sw (или какую-либо другую, вспомогательную функцию).
@ -185,7 +185,7 @@ apple = obj {
Начиная с версии 0.8.9 вы можете определить функцию isForSave(k), которая вызывается для определения необходимости записи переменной в файл сохранения. По умолчанию, функция определена следующим образом: Начиная с версии 0.8.9 вы можете определить функцию isForSave(k), которая вызывается для определения необходимости записи переменной в файл сохранения. По умолчанию, функция определена следующим образом:
<code> <code>
function isForSave(k) function isForSave(k)
return string.find(k, '_') == 1 or string.match(k,'^%u') return string.find(k, '_') == 1 or string.match(k,'^%u')
end end
</code> </code>
@ -194,20 +194,20 @@ end
Иногда может понадобиться обработчик, который совершал бы некоторое действие, но не выводил никакого описания. Например: Иногда может понадобиться обработчик, который совершал бы некоторое действие, но не выводил никакого описания. Например:
<code> <code>
button = obj { button = obj {
nam = "кнопка", nam = "кнопка",
dsc = "На стене комнаты видна большая красная {кнопка}.", dsc = "На стене комнаты видна большая красная {кнопка}.",
act = function (s) act = function (s)
here()._dynamic_dsc = [[После того как я нажал на кнопку, комната преобразилась. here()._dynamic_dsc = [[После того как я нажал на кнопку, комната преобразилась.
Книжный шкаф куда-то исчез вместе со столом и комодом, а на его месте Книжный шкаф куда-то исчез вместе со столом и комодом, а на его месте
появился странного вида аппарат.]]; появился странного вида аппарат.]];
return true; return true;
end, end,
} }
r12 = room { r12 = room {
nam = 'комната', nam = 'комната',
_dynamic_dsc = 'Я нахожусь в комнате.', _dynamic_dsc = 'Я нахожусь в комнате.',
dsc = function (s) return s._dynamic_dsc end, dsc = function (s) return s._dynamic_dsc end,
obj = {'button'} obj = {'button'}
} }
</code> </code>
В данном случае обработчик `act` нужен для того, чтобы поменять описание комнаты, и не нужно, чтобы чтобы он выводил результат действия. Для отключения результата можно вернуть из обработчика значение true -- это будет означать, что действие успешно выполнено, но не требует дополнительного описания. В данном случае обработчик `act` нужен для того, чтобы поменять описание комнаты, и не нужно, чтобы чтобы он выводил результат действия. Для отключения результата можно вернуть из обработчика значение true -- это будет означать, что действие успешно выполнено, но не требует дополнительного описания.
@ -323,12 +323,12 @@ knife = obj {
inv = 'Острый!', inv = 'Острый!',
tak = 'Я взял нож!', tak = 'Я взял нож!',
use = function(s, w) use = function(s, w)
if w ~= 'tabl' if w ~= 'tabl' then
return 'Не хочу это резать.', false return 'Не хочу это резать.', false
else else
return 'Вы вырезаете на столе свои инициалы.'; return 'Вы вырезаете на столе свои инициалы.';
end end
end end
}; };
</code> </code>
Нож можно использовать только на стол. Нож можно использовать только на стол.
@ -338,9 +338,9 @@ knife = obj {
Игрок в STEAD представлен объектом pl. Тип объекта -- player. В движке объект создается следующим образом: Игрок в STEAD представлен объектом pl. Тип объекта -- player. В движке объект создается следующим образом:
<code> <code>
pl = player { pl = player {
nam = "Incognito", nam = "Incognito",
where = 'main', where = 'main',
obj = { } obj = { }
}; };
</code> </code>
Атрибут obj представляет собой инвентарь игрока. Атрибут obj представляет собой инвентарь игрока.
@ -350,13 +350,13 @@ pl = player {
Игра также представлена объектом game с типом game. В движке он определяется следующим образом: Игра также представлена объектом game с типом game. В движке он определяется следующим образом:
<code> <code>
game = game { game = game {
nam = "INSTEAD -- Simple Text Adventure interpreter v"..version.." '2009 by Peter Kosyh", nam = "INSTEAD -- Simple Text Adventure interpreter v"..version.." '2009 by Peter Kosyh",
dsc = [[ dsc = [[
Commands:^ Commands:^
look(or just enter), act <on what> (or just what), use <what> [on what], go <where>,^ look(or just enter), act <on what> (or just what), use <what> [on what], go <where>,^
back, inv, way, obj, quit, save <fname>, load <fname>.]], back, inv, way, obj, quit, save <fname>, load <fname>.]],
pl ='pl', pl ='pl',
showlast = true, showlast = true,
}; };
</code> </code>
Как видим, объект хранит в себе указатель на текущего игрока ('pl') и некоторые параметры. Например, вы можете указать в начале своей игры кодировку текста следующим образом: Как видим, объект хранит в себе указатель на текущего игрока ('pl') и некоторые параметры. Например, вы можете указать в начале своей игры кодировку текста следующим образом:
@ -465,19 +465,19 @@ end
</code> </code>
move(o, w) -- переносит объект из текущей сцены в другую: move(o, w) -- переносит объект из текущей сцены в другую:
<code> move('mycat','inmycar');</code> <code>move('mycat','inmycar');</code>
Если вы хотите перенести объект из произвольной сцены, вам придется удалить его из старой сцены с помощью метода del. Для создания сложно перемещающихся объектов, вам придется написать свой метод, который будет сохранять текущую позицию объекта в самом объекте и делать удаление объекта из старой сцены. Вы можете указать исходную позицию (комнату) объекта в качестве третьего параметра move. Если вы хотите перенести объект из произвольной сцены, вам придется удалить его из старой сцены с помощью метода del. Для создания сложно перемещающихся объектов, вам придется написать свой метод, который будет сохранять текущую позицию объекта в самом объекте и делать удаление объекта из старой сцены. Вы можете указать исходную позицию (комнату) объекта в качестве третьего параметра move.
<code> move('mycat','inmycar', 'forest'); </code> <code>move('mycat','inmycar', 'forest'); </code>
Начиная с версии 0.8 присутствует также функция movef, аналогичная move, но добавляющая объект в начало списка. Начиная с версии 0.8 присутствует также функция movef, аналогичная move, но добавляющая объект в начало списка.
seen(o) -- если объект присутствует в текущей сцене: seen(o) -- если объект присутствует в текущей сцене:
<code> <code>
if seen('mycat') then if seen('mycat') then
move('mycat','inmycar'); move('mycat','inmycar');
end end
</code> </code>
Начиная с 0.8.6 -- необязательный второй параметр -- сцена. Начиная с 0.8.6 -- необязательный второй параметр -- сцена.
@ -505,11 +505,11 @@ change_pl(p) -- переключиться на другого игрока (с
<code> <code>
mycar = obj { mycar = obj {
nam = 'моя машина', nam = 'моя машина',
dsc = 'Перед хижиной стоит мой старенький {пикап} Toyota.', dsc = 'Перед хижиной стоит мой старенький {пикап} Toyota.',
act = function(s) act = function(s)
return goto('inmycar'); return goto('inmycar');
end end
}; };
</code> </code>
@ -526,41 +526,41 @@ par(...) -- возвращает строку -- склейку строк-ар
Диалоги это сцены, содержащие объекты -- фразы. Например, простейший диалог может выглядеть следующим образом. Диалоги это сцены, содержащие объекты -- фразы. Например, простейший диалог может выглядеть следующим образом.
<code> <code>
povardlg = dlg { povardlg = dlg {
nam = 'на кухне', nam = 'на кухне',
dsc = 'Передо мной полное лицо женщины - повара в белом колпаке и усталым взглядом...', dsc = 'Передо мной полное лицо женщины - повара в белом колпаке и усталым взглядом...',
obj = { obj = {
[1] = phr('Мне вот-этих зелененьких... Ага -- и бобов!', 'На здоровье!'), [1] = phr('Мне вот-этих зелененьких... Ага -- и бобов!', 'На здоровье!'),
[2] = phr('Картошку с салом, пожалуйста!', 'Приятного аппетита!'), [2] = phr('Картошку с салом, пожалуйста!', 'Приятного аппетита!'),
[3] = phr('Две порции чесночного супа!!!', 'Прекрасный выбор!'), [3] = phr('Две порции чесночного супа!!!', 'Прекрасный выбор!'),
[4] = phr('Мне что-нибудь легонькое, у меня язва...', 'Овсянка!'), [4] = phr('Мне что-нибудь легонькое, у меня язва...', 'Овсянка!'),
}, },
}; };
</code> </code>
phr -- создание фразы. Фраза содержит вопрос, ответ и реакцию (реакция в данном примере отсутствует). Когда игрок выбирает одну из фраз, фраза отключается. Когда все фразы отключатся диалог заканчивается. Реакция -- это строка кода на lua который выполнится после отключения фразы. Например: phr -- создание фразы. Фраза содержит вопрос, ответ и реакцию (реакция в данном примере отсутствует). Когда игрок выбирает одну из фраз, фраза отключается. Когда все фразы отключатся диалог заканчивается. Реакция -- это строка кода на lua который выполнится после отключения фразы. Например:
<code> <code>
food = obj { food = obj {
nam = 'еда', nam = 'еда',
inv = function (s) inv = function (s)
inv():del('food'); inv():del('food');
return 'Я ем.'; return 'Я ем.';
end end
}; };
gotfood = function(w) gotfood = function(w)
inv():add('food'); inv():add('food');
food._num = w; food._num = w;
return back(); return back();
end end
povardlg = dlg { povardlg = dlg {
nam = 'на кухне', nam = 'на кухне',
dsc = 'Передо мной полное лицо женщины - повара в белом колпаке и усталым взглядом...', dsc = 'Передо мной полное лицо женщины - повара в белом колпаке и усталым взглядом...',
obj = { obj = {
[1] = phr('Мне вот-этих зелененьких... Ага -- и бобов!', 'На здоровье!', [[pon(1); return gotfood(1);]]), [1] = phr('Мне вот-этих зелененьких... Ага -- и бобов!', 'На здоровье!', [[pon(1); return gotfood(1);]]),
[2] = phr('Картошку с салом, пожалуйста!', 'Приятного аппетита!', [[pon(2); return gotfood(2);]]), [2] = phr('Картошку с салом, пожалуйста!', 'Приятного аппетита!', [[pon(2); return gotfood(2);]]),
[3] = phr('Две порции чесночного супа!!!', 'Прекрасный выбор!', [[pon(3);return gotfood(3);]]), [3] = phr('Две порции чесночного супа!!!', 'Прекрасный выбор!', [[pon(3);return gotfood(3);]]),
[4] = phr('Мне что-нибудь легонькое, у меня язва...', 'Овсянка!', [[pon(4); return gotfood(4);]]), [4] = phr('Мне что-нибудь легонькое, у меня язва...', 'Овсянка!', [[pon(4); return gotfood(4);]]),
}, },
}; };
</code> </code>
В данном примере, игрок выбирает еду. Получае ее (запомнив выбор в переменной food._num) и возвращается обратно (в ту сцену откуда попал в диалог). В данном примере, игрок выбирает еду. Получае ее (запомнив выбор в переменной food._num) и возвращается обратно (в ту сцену откуда попал в диалог).
@ -586,23 +586,23 @@ povar = obj {
Также, вы можете прятать некоторые фразы при инициализации диалога и показывать их при некоторых условиях. Также, вы можете прятать некоторые фразы при инициализации диалога и показывать их при некоторых условиях.
<code> <code>
facectrl = dlg { facectrl = dlg {
nam = 'фэйсконтроль', nam = 'фэйсконтроль',
dsc = 'Я вижу перед собой неприятное лицо полного охранника.', dsc = 'Я вижу перед собой неприятное лицо полного охранника.',
obj = { obj = {
[1] = phr('Я пришел послушать лекцию Белина...', [1] = phr('Я пришел послушать лекцию Белина...',
'-- Я не знаю кто вы -- ухмыляется охранник -- но мне велели пускать сюда только приличных людей.', '-- Я не знаю кто вы -- ухмыляется охранник -- но мне велели пускать сюда только приличных людей.',
[[pon(2);]]), [[pon(2);]]),
[2] = _phr('У меня есть приглашение!', [2] = _phr('У меня есть приглашение!',
'-- А мне плевать! Посмотри на себя в зеркало!!! Ты пришел слушать самого Белина -- правую руку самого... -- охранник почтительно помолчал -- Так что пошел вон..', [[pon(3,4)]]), '-- А мне плевать! Посмотри на себя в зеркало!!! Ты пришел слушать самого Белина -- правую руку самого... -- охранник почтительно помолчал -- Так что пошел вон..', [[pon(3,4)]]),
[3] = _phr('Сейчас я дам тебе по роже!', '-- Ну все... Мощные руки выталкивают меня в коридор...', [3] = _phr('Сейчас я дам тебе по роже!', '-- Ну все... Мощные руки выталкивают меня в коридор...',
[[poff(4)]]), [[poff(4)]]),
[4] = _phr('Ты, кабан! Я же тебе сказал -- у меня есть приглашение!', [4] = _phr('Ты, кабан! Я же тебе сказал -- у меня есть приглашение!',
'-- Чтоооооо? Глаза охранника наливаются кровью... Мощный пинок отправляет меня в коридор...', '-- Чтоооооо? Глаза охранника наливаются кровью... Мощный пинок отправляет меня в коридор...',
[[poff(3)]]), [[poff(3)]]),
}, },
exit = function(s,w) exit = function(s,w)
s:pon(1); s:pon(1);
end, end,
}; };
</code> </code>
@ -615,19 +615,19 @@ facectrl = dlg {
Иногда, сцену нужно наполнить декорациями, которые обладают ограниченной функциональностью, но делают игру разнообразней. Для этого можно использовать облегченный объект. Например: Иногда, сцену нужно наполнить декорациями, которые обладают ограниченной функциональностью, но делают игру разнообразней. Для этого можно использовать облегченный объект. Например:
<code> <code>
sside = room { sside = room {
nam = 'южная сторона', nam = 'южная сторона',
dsc = [[Я нахожусь у южной стены здания института. ]], dsc = [[Я нахожусь у южной стены здания института. ]],
act = function(s, w) act = function(s, w)
if w == 1 then if w == 1 then
ways():add('stolcorridor'); ways():add('stolcorridor');
return "Я подошел к подъезду. На двери подъезда надпись -- 'Столовая'. Хм -- зайти внутрь?"; return "Я подошел к подъезду. На двери подъезда надпись -- 'Столовая'. Хм -- зайти внутрь?";
end end
if w == 2 then if w == 2 then
return 'Те, кто выходят, выглядят более довольными...'; return 'Те, кто выходят, выглядят более довольными...';
end end
end, end,
obj = { vobj(1, "подъезд", "У восточного угла находится небольшой {подъезд}."), obj = { vobj(1, "подъезд", "У восточного угла находится небольшой {подъезд}."),
vobj(2, "люди", "Время от времени дверь подъезда хлопает впуская и выпуская {людей}.")}, vobj(2, "люди", "Время от времени дверь подъезда хлопает впуская и выпуская {людей}.")},
}; };
</code> </code>
Как видим, vobj позволяет сделать легкую версию статического объекта, с которым тем не менее можно взаимодействовать (за счет определения обработчика act в сцене и анализа ключа объекта). vobj также вызывает метод used, при этом в качестве третьего параметра передается объект, воздействующий на виртуальный объект. Как видим, vobj позволяет сделать легкую версию статического объекта, с которым тем не менее можно взаимодействовать (за счет определения обработчика act в сцене и анализа ключа объекта). vobj также вызывает метод used, при этом в качестве третьего параметра передается объект, воздействующий на виртуальный объект.
@ -644,8 +644,8 @@ sside = room {
Вы можете динамически заполнять сцену объектами vobj или vway с помощью методов add и del. Например: Вы можете динамически заполнять сцену объектами vobj или vway с помощью методов add и del. Например:
<code> <code>
home.objs:add(vway("next", "{Дальше}.", 'next_room'); home.objs:add(vway("next", "{Дальше}.", 'next_room'));
-- some code here -- здесь какой-нибудь код
home.objs:del("next"); home.objs:del("next");
</code> </code>
@ -661,32 +661,32 @@ sside = room {
Вы можете определять обработчики, которые выполняются каждый раз, когда время игры увеличивается на 1. Например: Вы можете определять обработчики, которые выполняются каждый раз, когда время игры увеличивается на 1. Например:
<code> <code>
mycat = obj { mycat = obj {
nam = 'Барсик', nam = 'Барсик',
lf = { lf = {
[1] = 'Барсик шевелится у меня за пазухой.', [1] = 'Барсик шевелится у меня за пазухой.',
[2] = 'Барсик выглядывает из за пазухи.', [2] = 'Барсик выглядывает из-за пазухи.',
[3] = 'Барсик мурлычит у меня за пазухой.', [3] = 'Барсик мурлычит у меня за пазухой.',
[4] = 'Барсик дрожит у меня за пазухой.', [4] = 'Барсик дрожит у меня за пазухой.',
[5] = 'Я чувствую тепло Барсика у себя за пазухой.', [5] = 'Я чувствую тепло Барсика у себя за пазухой.',
[6] = 'Барсик высовывает голову из за пазухи и осматривает местность.', [6] = 'Барсик высовывает голову из-за пазухи и осматривает местность.',
}, },
life = function(s) life = function(s)
local r = rnd(6); local r = rnd(6);
if r > 2 then if r > 2 then
return; return;
end end
r = rnd(6); r = rnd(6);
return s.lf[r]; return s.lf[r];
end, end,
.... ....
profdlg2 = dlg { profdlg2 = dlg {
nam = 'Белин', nam = 'Белин',
dsc = 'Белин бледен. Он смотрит на дробовик рассеянным взглядом.', dsc = 'Белин бледен. Он смотрит на дробовик рассеянным взглядом.',
obj = { obj = {
[1] = phr('Я пришел за своим котом.', [1] = phr('Я пришел за своим котом.',
'Я выхватываю Барсика из руки Белина и засовываю себе за пазуху.', 'Я выхватываю Барсика из руки Белина и засовываю себе за пазуху.',
[[inv():add('mycat'); lifeon('mycat')]]), [[inv():add('mycat'); lifeon('mycat')]]),
.... ....
</code> </code>
Любой объект или сцена могут иметь свой обработчик life, который вызывается каждый раз при смене текущего времени игры, если объект или сцена были добавлены в список живых объектов с помощью lifeon. Не забывайте удалять живые объекты из списка с помощью lifeoff, когда они больше не нужны. Это можно сделать, например, в обработчике exit, или любым другим способом. Любой объект или сцена могут иметь свой обработчик life, который вызывается каждый раз при смене текущего времени игры, если объект или сцена были добавлены в список живых объектов с помощью lifeon. Не забывайте удалять живые объекты из списка с помощью lifeoff, когда они больше не нужны. Это можно сделать, например, в обработчике exit, или любым другим способом.
@ -705,9 +705,9 @@ return 'В комнату вошел охранник.', true
<code> <code>
home = room { home = room {
pic = 'gfx/home.png', pic = 'gfx/home.png',
nam = 'дома', nam = 'дома',
dsc = 'Я у себя дома', dsc = 'Я у себя дома',
}; };
</code> </code>
@ -719,7 +719,7 @@ home = room {
Начиная с версии 0.9.2 вы можете встраивать графические изображения в текст или в инвентарь с помощью функции img. Например: Начиная с версии 0.9.2 вы можете встраивать графические изображения в текст или в инвентарь с помощью функции img. Например:
<code> <code>
knife = obj { knife = obj {
nam = 'Нож'..img('img/knife.png'), nam = 'Нож'..img('img/knife.png'),
} }
</code> </code>
@ -734,12 +734,12 @@ set_music(имя музыкального файла).
Например: Например:
<code> <code>
street = room { street = room {
pic = 'gfx/street.png', pic = 'gfx/street.png',
enter = function() enter = function()
set_music('mus/rain.ogg'); set_music('mus/rain.ogg');
end, end,
nam = 'на улице', nam = 'на улице',
dsc = 'На улице идет дождь.', dsc = 'На улице идет дождь.',
}; };
</code> </code>
@ -764,8 +764,8 @@ is_music() позволяет узнать, проигрывается ли му
Например: Например:
<code> <code>
main = room { main = room {
nam = 'Intro', nam = 'Intro',
dsc = txtc('Добро пожаловать!'), dsc = txtc('Добро пожаловать!'),
} }
</code> </code>
@ -778,8 +778,8 @@ main = room {
Например: Например:
<code> <code>
main = room { main = room {
nam = 'Intro', nam = 'Intro',
dsc = 'Вы находитесь в комнате '..txtb('main')..'.', dsc = 'Вы находитесь в комнате '..txtb('main')..'.',
} }
Начиная с версии 1.1.0 вы также можете создавать неразрываемые строки с помощью: txtnb(); Начиная с версии 1.1.0 вы также можете создавать неразрываемые строки с помощью: txtnb();
@ -789,33 +789,33 @@ main = room {
Вы можете делать меню в области инвентаря, определяя объекты с типом menu. При этом, обработчик меню будет вызван после одного клика мыши. Если обработчик не возвращает текст, то состояние игры не изменяется. Например, реализация кармана: Вы можете делать меню в области инвентаря, определяя объекты с типом menu. При этом, обработчик меню будет вызван после одного клика мыши. Если обработчик не возвращает текст, то состояние игры не изменяется. Например, реализация кармана:
<code> <code>
pocket = menu { pocket = menu {
State = false, State = false,
nam = function(s) nam = function(s)
if s.State then if s.State then
return txtu('карман'); return txtu('карман');
end end
return 'карман'; return 'карман';
end, end,
gen = function(s) gen = function(s)
if s.State then if s.State then
s:enable_all(); s:enable_all();
else else
s:disable_all(); s:disable_all();
end end
end, end,
menu = function(s) menu = function(s)
if s.State then if s.State then
s.State = false; s.State = false;
else else
s.State = true; s.State = true;
end end
s:gen(); s:gen();
end, end,
}; };
knife = obj { knife = obj {
nam = 'нож', nam = 'нож',
inv = 'Это нож', inv = 'Это нож',
}; };
inv():add(pocket); inv():add(pocket);
@ -823,7 +823,7 @@ put(knife, pocket);
pocket:gen(); pocket:gen();
main = room { main = room {
nam = 'test', nam = 'test',
}; };
</code> </code>
@ -835,9 +835,9 @@ pl.Life = 10;
pl.Power = 10; pl.Power = 10;
status = obj { status = obj {
nam = function(s) nam = function(s)
return 'Жизнь: '..pl.Life..',Сила: '..pl.Power return 'Жизнь: '..pl.Life..',Сила: '..pl.Power
end end
}; };
inv():add('status'); inv():add('status');
status.object_type = false status.object_type = false
@ -849,9 +849,9 @@ pl.Life = 10;
pl.Power = 10; pl.Power = 10;
status = stat { status = stat {
nam = function(s) nam = function(s)
return 'Жизнь: '..pl.Life..',Сила: '..pl.Power return 'Жизнь: '..pl.Life..',Сила: '..pl.Power
end end
}; };
inv():add('status'); inv():add('status');
</code> </code>
@ -861,8 +861,9 @@ inv():add('status');
Если вы выполните goto из обработчика exit, то получите переполнение стека, так как goto снова и снова будет вызывать метод exit. Вы можете избавиться от этого, если вставите проверку, разрушающую рекурсию. Например: Если вы выполните goto из обработчика exit, то получите переполнение стека, так как goto снова и снова будет вызывать метод exit. Вы можете избавиться от этого, если вставите проверку, разрушающую рекурсию. Например:
<code> <code>
exit = function(s, t) exit = function(s, t)
if t == 'dialog' then return; end if t == 'dialog' then return; end
return goto('dialog'); return goto('dialog');
end
</code> </code>
Начиная с версии 0.9.1 движок сам разрывает рекурсию. Начиная с версии 0.9.1 движок сам разрывает рекурсию.
@ -880,27 +881,27 @@ home.obj:del('Дорога');
Для определения наличия ссылки в сцене -- метод srch. Для определения наличия ссылки в сцене -- метод srch.
<code> <code>
if not home.obj:srch('Дорога') then if not home.obj:srch('Дорога') then
home.obj:add(vway('Дорога', 'Я заметил {дорогу}, ведущую в лес...', 'forest')); home.obj:add(vway('Дорога', 'Я заметил {дорогу}, ведущую в лес...', 'forest'));
end end
</code> </code>
Динамические ссылки удобно создавать в обработчике enter, или по мере необходимости в любом месте кода игры. Если ссылки создаются в текущей сцене, то последний пример можно упростить: Динамические ссылки удобно создавать в обработчике enter, или по мере необходимости в любом месте кода игры. Если ссылки создаются в текущей сцене, то последний пример можно упростить:
<code> <code>
if not seen('Дорога') then if not seen('Дорога') then
objs():add(vway('Дорога', 'Я заметил {дорогу}, ведущую в лес...', 'forest')); objs():add(vway('Дорога', 'Я заметил {дорогу}, ведущую в лес...', 'forest'));
end end
</code> </code>
Кроме того, вы можете просто включать и выключать ссылки с помощью enable(), disable(), например: Кроме того, вы можете просто включать и выключать ссылки с помощью enable(), disable(), например:
<code> <code>
objs()[1]:disable(); objs()[1]:disable();
</code> </code>
Вы можете создавать выключенные vobj и vway следующим образом: Вы можете создавать выключенные vobj и vway следующим образом:
<code> <code>
obj = {vway('Дорога', 'Я заметил {дорогу}, ведущую в лес...', 'forest'):disable()}, obj = {vway('Дорога', 'Я заметил {дорогу}, ведущую в лес...', 'forest'):disable()},
</code> </code>
И затем включать их по индексу в массиве obj: И затем включать их по индексу в массиве obj:
<code> <code>
objs()[1]:enable(); objs()[1]:enable();
</code> </code>
==== Кодирование исходного кода игры (начиная с версии 0.9.3) ==== ==== Кодирование исходного кода игры (начиная с версии 0.9.3) ====
@ -922,22 +923,23 @@ doencfile("game");
==== Использование первого параметра обработчика ==== ==== Использование первого параметра обработчика ====
Пример кода. Пример кода.
<code> <code>
knife = obj { stone = obj {
nam = 'камень', nam = 'камень',
dsc = 'На краю лежит {камень}.', dsc = 'На краю лежит {камень}.',
act = function() act = function()
objs():del('knife'); objs():del('stone');
return 'Я толкнул камень, он сорвался и улетел вниз...'; return 'Я толкнул камень, он сорвался и улетел вниз...';
end end
</code> </code>
Обработчик act мог бы выглядеть проще: Обработчик act мог бы выглядеть проще:
<code> <code>
act = function(s) act = function(s)
objs():del(s); objs():del(s);
return 'Я толкнул камень, он сорвался и улетел вниз...'; return 'Я толкнул камень, он сорвался и улетел вниз...';
end end
</code> </code>
==== Использование set_music ==== ==== Использование set_music ====
Вы можете использовать set_music для проигрывания звуков, задавая второй параметр -- счетчик циклов проигрывания звукового файла. Вы можете использовать set_music для проигрывания звуков, задавая второй параметр -- счетчик циклов проигрывания звукового файла.
@ -946,20 +948,20 @@ knife = obj {
-- играет треки в случайном порядке, начиная со 2-го -- играет треки в случайном порядке, начиная со 2-го
tracks = {"mus/astro2.mod", "mus/aws_chas.xm", "mus/dmageofd.xm", "mus/doomsday.s3m"} tracks = {"mus/astro2.mod", "mus/aws_chas.xm", "mus/dmageofd.xm", "mus/doomsday.s3m"}
mplayer = obj { mplayer = obj {
nam = 'плеер', nam = 'плеер',
life = function(s) life = function(s)
local n = get_music(); local n = get_music();
local v = get_music_loop(); local v = get_music_loop();
if not n or not v then if not n or not v then
set_music(tracks[2], 1); set_music(tracks[2], 1);
elseif v == -1 then elseif v == -1 then
local n = get_music(); local n = get_music();
while get_music() == n do while get_music() == n do
n = tracks[rnd(4)] n = tracks[rnd(4)]
end end
set_music(n, 1); set_music(n, 1);
end end
end, end,
}; };
lifeon('mplayer'); lifeon('mplayer');
</code> </code>
@ -968,20 +970,20 @@ lifeon('mplayer');
<code> <code>
function save_music(s) function save_music(s)
s.OldMusic = get_music(); s.OldMusic = get_music();
s.OldMusicLoop = get_music_loop(); s.OldMusicLoop = get_music_loop();
end end
function restore_music(s) function restore_music(s)
set_music(s.OldMusic, s.OldMusicLoop); set_music(s.OldMusic, s.OldMusicLoop);
end end
-- .... -- ....
enter = function(s) enter = function(s)
save_music(s); save_music(s);
end, end,
exit = function(s) exit = function(s)
restore_music(s); restore_music(s);
end, end,
-- .... -- ....
@ -993,17 +995,18 @@ end,
Если вашему герою нужен друг, одним из способов может стать метод life этого персонажа, который всегда переносит объект в локацию игрока: Если вашему герою нужен друг, одним из способов может стать метод life этого персонажа, который всегда переносит объект в локацию игрока:
<code> <code>
horse = obj { horse = obj {
nam = 'лошадь', nam = 'лошадь',
dsc = 'Рядом со мной стоит {лошадь}.', dsc = 'Рядом со мной стоит {лошадь}.',
life = function(s) life = function(s)
if not seen('horse') then if not seen('horse') then
move('horse', here(), s.__where); move('horse', here(), s.__where);
s.__where = pl.where; s.__where = pl.where;
end end
end, end,
}; };
lifeon('horse'); lifeon('horse');
</code> </code>
==== Таймер ==== ==== Таймер ====
Начиная с версии 1.1.0 в instead появлилась возможность использовать таймер. (Только в графической версии интерпретатора.) Начиная с версии 1.1.0 в instead появлилась возможность использовать таймер. (Только в графической версии интерпретатора.)
@ -1025,7 +1028,7 @@ main = room {
force_dsc = true, force_dsc = true,
nam = 'Таймер', nam = 'Таймер',
dsc = function(s) dsc = function(s)
return 'Демонстрация: '..tostring(s._time); return 'Демонстрация: '..tostring(s._time);
end end
}; };
</code> </code>
@ -1039,26 +1042,26 @@ input.key(s, pressed, key) -- обработчик клавиатуры; pressed
Например: Например:
<code> <code>
input.key = function(s, pr, key) input.key = function(s, pr, key)
if not pr or key == "escape"then if not pr or key == "escape"then
return return
elseif key == 'space' then elseif key == 'space' then
key = ' ' key = ' '
elseif key == 'return' then elseif key == 'return' then
key = '^'; key = '^';
end end
if key:len() > 1 then return end if key:len() > 1 then return end
main._txt = main._txt:gsub('_$',''); main._txt = main._txt:gsub('_$','');
main._txt = main._txt..key..'_'; main._txt = main._txt..key..'_';
return "look"; return "look";
end end
main = room { main = room {
_txt = '_', _txt = '_',
force_dsc = true, force_dsc = true,
nam = 'Клавиатура', nam = 'Клавиатура',
dsc = function(s) dsc = function(s)
return 'Демонстрация: '..tostring(s._txt); return 'Демонстрация: '..tostring(s._txt);
end end
}; };
</code> </code>
@ -1067,7 +1070,7 @@ main = room {
input.click(s, pressed, mb, x, y, px, py) -- обработчик клика мыши; pressed -- нажатие или отжатие. mb -- номер кнопки (1 - левая), x и y -- координаты клика относительно левого верхнего угла. px и py присутствуют, если клик произошел в области картинки сцены и содержит координаты клика относительно левого верхнего угла картинки. input.click(s, pressed, mb, x, y, px, py) -- обработчик клика мыши; pressed -- нажатие или отжатие. mb -- номер кнопки (1 - левая), x и y -- координаты клика относительно левого верхнего угла. px и py присутствуют, если клик произошел в области картинки сцены и содержит координаты клика относительно левого верхнего угла картинки.
Обработчик может вернуть команду интерфейса stead, в этом случае клик не будет обработана интерпретатором. Обработчик может вернуть команду интерфейса stead, в этом случае клик не будет обработан интерпретатором.
Например: Например:
<code> <code>
@ -1135,10 +1138,10 @@ delete(n)
new воспринимает строку-аргумент как конструктор объекта. Результатом выполнения конструктора должен быть объект. Таким образом в аргументе обычно задан вызов функции-конструктора. Например: new воспринимает строку-аргумент как конструктор объекта. Результатом выполнения конструктора должен быть объект. Таким образом в аргументе обычно задан вызов функции-конструктора. Например:
<code> <code>
function myconstructor() function myconstructor()
local v = {} local v = {}
v.nam = 'тестовый объект', v.nam = 'тестовый объект',
v.act = 'Тестовая реакция', v.act = 'Тестовая реакция',
return obj(v); return obj(v);
end end
</code> </code>
Созданный объект будет попадать в файл сохранения. new() возвращает реальный объект; чтобы получить его имя, если это нужно, используйте функцию deref: Созданный объект будет попадать в файл сохранения. new() возвращает реальный объект; чтобы получить его имя, если это нужно, используйте функцию deref:
@ -1150,24 +1153,26 @@ delete(o_name);
Иногда вывод обработчика может формироваться сложным образом, в зависимости от условий. В таких случаях удобно пользоваться функциями p() и pn(). Эти функции добавляют текст в буфер, связанный с обработчиком, который будет возвращен из обработчика. Иногда вывод обработчика может формироваться сложным образом, в зависимости от условий. В таких случаях удобно пользоваться функциями p() и pn(). Эти функции добавляют текст в буфер, связанный с обработчиком, который будет возвращен из обработчика.
<code> <code>
dsc = function(s) dsc = function(s)
p "На полу стоит {бочка}." p "На полу стоит {бочка}."
if s._opened then if s._opened then
p "Крышка от бочки лежит рядом." p "Крышка от бочки лежит рядом."
end end
end end
</code> </code>
Функция pn() выполняет вывод текста в буфер, дополняя его переводом строки. Функция p() дополняет вывод пробелом. Функция pn() выполняет вывод текста в буфер, дополняя его переводом строки. Функция p() дополняет вывод пробелом.
Начиная с версии 1.1.6 существует функция pr(), которая не выполняет дополнение вывода.
Для очистки буфера, используйте pclr(). Если вам нужно вернуть статус действия, используйте pget(). Для очистки буфера, используйте pclr(). Если вам нужно вернуть статус действия, используйте pget().
<code> <code>
use = function(s, w) use = function(s, w)
if w == 'apple' then if w == 'apple' then
p 'Гм... Я почистил яблоко.'; p 'Гм... Я почистил яблоко.';
apple._peeled = true apple._peeled = true
return return
end end
p 'Это нельзя использовать так!' p 'Это нельзя использовать так!'
return pget(), false return pget(), false
end end
</code> </code>