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:
<code>
function sw(v, a, b)
if v then
return a;
end
return b
if v then
return a;
end
return b
end
aple = obj {
nam = function(s)
return sw(not s._seen, 'unknown','apple');
end,
dsc = function(s)
return sw(not s._seen,'There is {something} on the table.', 'There is an {apple} on the table.');
end,
act = function(s)
if s._seen then
return 'It\'s an apple!';
else
s._seen = true;
return 'Hm... But it\'s an apple!';
end
end,
nam = function(s)
return sw(not s._seen, 'unknown','apple');
end,
dsc = function(s)
return sw(not s._seen,'There is {something} on the table.', 'There is an {apple} on the table.');
end,
act = function(s)
if s._seen then
return 'It\'s an apple!';
else
s._seen = true;
return 'Hm... But it\'s an apple!';
end
end,
};
</code>
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:
<code>
function isForSave(k)
return string.find(k, '_') == 1 or string.match(k,'^%u')
return string.find(k, '_') == 1 or string.match(k,'^%u')
end
</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.:
<code>
button = obj {
nam = "button",
dsc = "There is a big red {button} on the room wall.",
act = function (s)
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
looking device took its place.]];
return true;
end,
nam = "button",
dsc = "There is a big red {button} on the room wall.",
act = function (s)
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
looking device took its place.]];
return true;
end,
}
r12 = room {
nam = 'room',
_dynamic_dsc = 'I am in the room.',
dsc = function (s) return s._dynamic_dsc end,
obj = {'button'}
nam = 'room',
_dynamic_dsc = 'I am in the room.',
dsc = function (s) return s._dynamic_dsc end,
obj = {'button'}
}
</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.
@ -271,7 +271,7 @@ room2 = room {
};
</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>
room2 = room {
enter = function(s, f)
@ -325,7 +325,7 @@ knife = obj {
inv = 'Sharp!',
tak = 'I took the knife!',
use = function(s, w)
if w ~= 'tabl'
if w ~= 'tabl' then
return 'I don\'t want to cut this.', false
else
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:
<code>
pl = player {
nam = "Incognito",
where = 'main',
obj = { }
nam = "Incognito",
where = 'main',
obj = { }
};
</code>
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:
<code>
game = game {
nam = "INSTEAD -- Simple Text Adventure interpreter v"..version.." '2009 by Peter Kosyh",
dsc = [[
nam = "INSTEAD -- Simple Text Adventure interpreter v"..version.." '2009 by Peter Kosyh",
dsc = [[
Commands:^
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>.]],
pl ='pl',
showlast = true,
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:
<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.
@ -469,11 +469,11 @@ end
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”.
<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.
@ -509,11 +509,11 @@ change_pl(p) — switch to another player (with one's own inventory and position
<code>
mycar = obj {
nam = 'my car',
dsc = 'In front of the cabin there is my old Toyota {pickup}.',
act = function(s)
return goto('inmycar');
end
nam = 'my car',
dsc = 'In front of the cabin there is my old Toyota {pickup}.',
act = function(s)
return goto('inmycar');
end
};
</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:
<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...',
obj = {
[1] = phr('“Those green, please... Yeah, and beans too!”', '“Enjoy!”'),
[2] = phr('“Fried potato with lard, please!”', '“Bon appetit!”'),
[3] = phr('“Two helpings of garlic sooup!!!”', '“Good choice!”'),
[4] = phr('“Something light, please, I've got an ulcer...”', '“Oatmeal!”'),
},
nam = 'in the kitchen',
dsc = 'I see a fat face of a lady cook wearing a white hat with a tired look...',
obj = {
[1] = phr('“Those green, please... Yeah, and beans too!”', '“Enjoy!”'),
[2] = phr('“Fried potato with lard, please!”', '“Bon appetit!”'),
[3] = phr('“Two helpings of garlic sooup!!!”', '“Good choice!”'),
[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)
inv():del('food');
return 'I eat.';
end
nam = 'food',
inv = function (s)
inv():del('food');
return 'I eat.';
end
};
gotfood = function(w)
inv():add('food');
food._num = w;
return back();
inv():add('food');
food._num = w;
return back();
end
povardlg = dlg {
nam = 'in the kitchen',
dsc = 'I see a fat face of a lady cook wearing a white hat with a tired look...',
obj = {
[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);]]),
[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);]]),
},
nam = 'in the kitchen',
dsc = 'I see a fat face of a lady cook wearing a white hat with a tired look...',
obj = {
[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);]]),
[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);]]),
},
};
</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.
@ -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.
<code>
facectrl = dlg {
nam = 'фэйсконтроль',
dsc = 'I see an unpleasant face of a fat guard.',
obj = {
[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.”',
[[pon(2);]]),
[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)]]),
[3] = _phr(' “I\'m gonna kick your ass!”', '“I\'ve had enough...” Strong arms push me out to the corridor...',
[[poff(4)]]),
[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...',
[[poff(3)]]),
},
exit = function(s,w)
s:pon(1);
end,
nam = 'facecontrol',
dsc = 'I see an unpleasant face of a fat guard.',
obj = {
[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.”',
[[pon(2);]]),
[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)]]),
[3] = _phr(' “I\'m gonna kick your ass!”', '“I\'ve had enough...” Strong arms push me out to the corridor...',
[[poff(4)]]),
[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...',
[[poff(3)]]),
},
exit = function(s,w)
s:pon(1);
end,
};
</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:
<code>
sside = room {
nam = 'southern side',
dsc = [[I am near the southern wall of an institute building. ]],
act = function(s, w)
if w == 1 then
ways():add('stolcorridor');
return "I walked to the porch. The sign on the door read 'Canteen'. Hm... should I get in?";
end
if w == 2 then
return 'The ones going out look happier...';
end
end,
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..")},
nam = 'southern side',
dsc = [[I am near the southern wall of an institute building. ]],
act = function(s, w)
if w == 1 then
ways():add('stolcorridor');
return "I walked to the porch. The sign on the door read 'Canteen'. Hm... should I get in?";
end
if w == 2 then
return 'The ones going out look happier...';
end
end,
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.
@ -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:
<code>
home.objs:add(vway("next", "{Next}.", 'next_room');
home.objs:add(vway("next", "{Next}.", 'next_room'));
-- some code here
home.objs:del("next");
</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.:
<code>
mycat = obj {
nam = 'Barsik',
lf = {
[1] = 'Barsik is moving in my bosom.',
[2] = 'Barsik peers out of my bosom.',
[3] = 'Barsik purrs in my bosom.',
[4] = 'Barsik shivers in my bosom.',
[5] = 'I feel Barsik's warmth in my bosom.',
[6] = 'Barsik leans out of my bosom and looks around.',
},
life = function(s)
local r = rnd(6);
if r > 2 then
return;
end
r = rnd(6);
return s.lf[r];
end,
nam = 'Barsik',
lf = {
[1] = 'Barsik is moving in my bosom.',
[2] = 'Barsik peers out of my bosom.',
[3] = 'Barsik purrs in my bosom.',
[4] = 'Barsik shivers in my bosom.',
[5] = 'I feel Barsik's warmth in my bosom.',
[6] = 'Barsik leans out of my bosom and looks around.',
},
life = function(s)
local r = rnd(6);
if r > 2 then
return;
end
r = rnd(6);
return s.lf[r];
end,
....
profdlg2 = dlg {
nam = 'Belin',
dsc = 'Belin is pale. He absently looks at the shotgun.',
obj = {
[1] = phr('“I came for my cat.”',
'I snatch Barsik from Belin's hand and put in my bosom.',
[[inv():add('mycat'); lifeon('mycat')]]),
nam = 'Belin',
dsc = 'Belin is pale. He absently looks at the shotgun.',
obj = {
[1] = phr('“I came for my cat.”',
'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.
@ -709,9 +709,9 @@ Graphic interpreter analyzes the scene “pic” attribute and treats it as a pa
<code>
home = room {
pic = 'gfx/home.png',
nam = 'at home',
dsc = 'I am at home',
pic = 'gfx/home.png',
nam = 'at home',
dsc = 'I am at home',
};
</code>
@ -724,7 +724,7 @@ From version 0.9.2 graphics can be embedded everywhere in text or inventory with
<code>
knife = obj {
nam = 'Knife'..img('img/knife.png'),
nam = 'Knife'..img('img/knife.png'),
}
</code>
@ -733,12 +733,12 @@ The interpreter cycles the current music defined by the function ”set_music(mu
For example:
<code>
street = room {
pic = 'gfx/street.png',
enter = function()
set_music('mus/rain.ogg');
end,
nam = 'on the street',
dsc = 'It is raining outside.',
pic = 'gfx/street.png',
enter = function()
set_music('mus/rain.ogg');
end,
nam = 'on the street',
dsc = 'It is raining outside.',
};
</code>
@ -771,8 +771,8 @@ txtl() - left align;
For example:
<code>
main = room {
nam = 'Intro',
dsc = txtc('Welcome!'),
nam = 'Intro',
dsc = txtc('Welcome!'),
}
</code>
@ -785,8 +785,8 @@ txtu() - underline;
For example:
<code>
main = room {
nam = 'Intro',
dsc = 'You are in the room: '..txtb('main')..'.',
nam = 'Intro',
dsc = 'You are in the room: '..txtb('main')..'.',
}
</code>
@ -795,8 +795,8 @@ Since the version 1.1.0 you can create unwrapped strings by using txtnb();
For example:
<code>
main = room {
nam = 'Intro',
dsc = 'You are in the room '..txtb('main')..'.',
nam = 'Intro',
dsc = 'You are in the room '..txtb('main')..'.',
}
</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:
<code>
pocket = menu {
State = false,
nam = function(s)
if s.State then
return txtu('pocket');
end
return 'pocket';
end,
gen = function(s)
if s.State then
s:enable_all();
else
s:disable_all();
end
end,
menu = function(s)
if s.State then
s.State = false;
else
s.State = true;
end
s:gen();
end,
State = false,
nam = function(s)
if s.State then
return txtu('pocket');
end
return 'pocket';
end,
gen = function(s)
if s.State then
s:enable_all();
else
s:disable_all();
end
end,
menu = function(s)
if s.State then
s.State = false;
else
s.State = true;
end
s:gen();
end,
};
knife = obj {
nam = 'knife',
inv = 'This is knife',
nam = 'knife',
inv = 'This is knife',
};
inv():add(pocket);
@ -838,7 +838,7 @@ put(knife, pocket);
pocket:gen();
main = room {
nam = 'test',
nam = 'test',
};
</code>
==== Player status ====
@ -849,9 +849,9 @@ pl.Life = 10;
pl.Power = 10;
status = obj {
nam = function(s)
return 'Life: '..pl.Life..',Power: '..pl.Power
end
nam = function(s)
return 'Life: '..pl.Life..',Power: '..pl.Power
end
};
inv():add('status');
status.object_type = false
@ -863,9 +863,9 @@ pl.Life = 10;
pl.Power = 10;
status = stat {
nam = function(s)
return 'Life: '..pl.Life..',Power: '..pl.Power
end
nam = function(s)
return 'Life: '..pl.Life..',Power: '..pl.Power
end
};
inv():add('status');
</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:
<code>
exit = function(s, t)
if t == 'dialog' then return; end
return goto('dialog');
if t == 'dialog' then return; end
return goto('dialog');
end
</code>
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.
<code>
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
</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'));
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();
objs()[1]:disable();
</code>
Creating disabled “vobj” and “vway”:
<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>
And then enabling them by their index in the “obj” array:
<code>
objs()[1]:enable();
objs()[1]:enable();
</code>
==== 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 ====
Code example.
<code>
knife = obj {
nam = 'stone',
dsc = 'There is a {stone} at the edge.',
act = function()
objs():del('knife');
return 'I pushed the stone, it fell and flew down...';
end
stone = obj {
nam = 'stone',
dsc = 'There is a {stone} at the edge.',
act = function()
objs():del('stone');
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
act = function(s)
objs():del(s);
return 'I pushed the stone, it fell and flew down...';
end
</code>
==== 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
tracks = {"mus/astro2.mod", "mus/aws_chas.xm", "mus/dmageofd.xm", "mus/doomsday.s3m"}
mplayer = obj {
nam = 'плеер',
life = function(s)
local n = get_music();
local v = get_music_loop();
if not n or not v then
set_music(tracks[2], 1);
elseif v == -1 then
local n = get_music();
while get_music() == n do
n = tracks[rnd(4)]
end
set_music(n, 1);
end
end,
nam = 'media player',
life = function(s)
local n = get_music();
local v = get_music_loop();
if not n or not v then
set_music(tracks[2], 1);
elseif v == -1 then
local n = get_music();
while get_music() == n do
n = tracks[rnd(4)]
end
set_music(n, 1);
end
end,
};
lifeon('mplayer');
</code>
@ -984,20 +985,20 @@ You can use “get_music_loop” and “get_music” functions to remember the l
<code>
function save_music(s)
s.OldMusic = get_music();
s.OldMusicLoop = get_music_loop();
s.OldMusic = get_music();
s.OldMusicLoop = get_music_loop();
end
function restore_music(s)
set_music(s.OldMusic, s.OldMusicLoop);
set_music(s.OldMusic, s.OldMusicLoop);
end
-- ....
enter = function(s)
save_music(s);
save_music(s);
end,
exit = function(s)
restore_music(s);
restore_music(s);
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:
<code>
horse = obj {
nam = 'horse',
dsc = 'A {horse} is standing next to me.',
life = function(s)
if not seen('horse') then
move('horse', here(), s.__where);
s.__where = pl.where;
end
end,
nam = 'horse',
dsc = 'A {horse} is standing next to me.',
life = function(s)
if not seen('horse') then
move('horse', here(), s.__where);
s.__where = pl.where;
end
end,
};
lifeon('horse');
</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:
<code>
timer.callback = function(s)
main._time = main._time + 1;
return "look";
main._time = main._time + 1;
return "look";
end
timer:set(100);
main = room {
_time = 1,
force_dsc = true,
nam = 'Timer',
dsc = function(s)
return 'Example: '..tostring(s._time);
end
_time = 1,
force_dsc = true,
nam = 'Timer',
dsc = function(s)
return 'Example: '..tostring(s._time);
end
};
</code>
@ -1055,26 +1056,26 @@ Handler can return a ''stead'' interface command. In this case the interpreter d
For example:
<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";
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 = 'Keyboard',
dsc = function(s)
return 'Example: '..tostring(s._txt);
end
_txt = '_',
force_dsc = true,
nam = 'Keyboard',
dsc = function(s)
return 'Example: '..tostring(s._txt);
end
};
</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:
<code>
function myconstructor()
local v = {}
v.nam = 'test object',
v.act = 'test feedback',
return obj(v);
local v = {}
v.nam = 'test object',
v.act = 'test feedback',
return obj(v);
end
</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:
@ -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.
<code>
dsc = function(s)
p "There is a {barrel} standing on the floor."
if s._opened then
p "The barrel lid lies nearby."
end
p "There is a {barrel} standing on the floor."
if s._opened then
p "The barrel lid lies nearby."
end
end
</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.
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()''.
<code>
use = function(s, w)
if w == 'apple' then
p 'I peeled the apple';
apple._peeled = true
return
end
p 'You cannot use it this way!'
return pget(), false
if w == 'apple' then
p 'I peeled the apple';
apple._peeled = true
return
end
p 'You cannot use it this way!'
return pget(), false
end
</code>

View file

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