[TADS] restart, death, and "lives"

9 views
Skip to first unread message

Wildman, the Cuberstalker

unread,
Nov 6, 1998, 3:00:00 AM11/6/98
to
I am in the process of writing a perl script that converts AGT games
(decompiled with agtout) into TADS source files. The script can't handle
every situation (metacommands need to be converted by hand) but the main
thing holding me up the different ways death and restarts is handled between
the two. In AGT, a player can have a limited number of lives. Is there a way
to keep track of how many times the player has died in TADS, or the game
restarted, or some other way to handle this? Keeping track of the initial
state of all objects, then resetting them (except for the lives counter)
without using restart() is rather impractical.

FWIW I was doing a MiSTing of 'Space Aliens Laughed at my Cardigan'. I
decided I wanted to work with a straight port. I soon realized that doing it
by hand was a major pain. I eventually got carried away. :)

--
Wildman, the Cuberstalker
You know the Klingon proverb that tells whose revenge is a dish that is best
served cold? It is very cold....in Cuberspace.
Fight spam - http://www.cauce.org/
DO NOT SPAM THIS ADDRESS

Roger Carbol

unread,
Nov 6, 1998, 3:00:00 AM11/6/98
to
Wildman, the Cuberstalker wrote:

> Is there a way
> to keep track of how many times the player has died in TADS, or the game
> restarted, or some other way to handle this? Keeping track of the initial
> state of all objects, then resetting them (except for the lives counter)
> without using restart() is rather impractical.


I'm inclined to recommend you consider writing-to/reading-from an
external file, using the TADS file operations. An ugly solution,
but it's an ugly problem.


.. Roger Carbol .. r...@shaw.wave.ca .. write only memory

Wildman, the Cuberstalker

unread,
Nov 6, 1998, 3:00:00 AM11/6/98
to
On Fri, 06 Nov 1998 09:06:10 +0000, Roger Carbol <r...@shaw.wave.ca> wrote:
>Wildman, the Cuberstalker wrote:
>
>> Is there a way
>> to keep track of how many times the player has died in TADS, or the game
>> restarted, or some other way to handle this? Keeping track of the initial
>> state of all objects, then resetting them (except for the lives counter)
>> without using restart() is rather impractical.
>
>
>I'm inclined to recommend you consider writing-to/reading-from an
>external file, using the TADS file operations. An ugly solution,
>but it's an ugly problem.

I'll take that into consideration. There really doesn't seem to be another
solution, other than hacking the TADS runtime (undesirable for obvious
reasons) - yet writing/reading an external file is only slightly more
desirable.

Thanks for the suggestion.

Andrew Plotkin

unread,
Nov 6, 1998, 3:00:00 AM11/6/98
to
Wildman, the Cuberstalker (wil...@microserve.net) wrote:
> >
> >> Is there a way
> >> to keep track of how many times the player has died in TADS, or the game
> >> restarted, or some other way to handle this? Keeping track of the initial
> >> state of all objects, then resetting them (except for the lives counter)
> >> without using restart() is rather impractical.

And yet, a surprising number of people have done it. :) TADS or Inform,
but the idea is the same -- for every property of an object that might
change, including location, keep a backup property, and a write a routine
to restore them all.

It's a pain in the ass, but a simple concept nevertheless.

--Z

--

"And Aholibamah bare Jeush, and Jaalam, and Korah: these were the
borogoves..."

Dan Shiovitz

unread,
Nov 6, 1998, 3:00:00 AM11/6/98
to
In article <slrn7455fi....@foobar.net>,

Wildman, the Cuberstalker <wil...@microserve.net> wrote:
>I am in the process of writing a perl script that converts AGT games
>(decompiled with agtout) into TADS source files. The script can't handle
>every situation (metacommands need to be converted by hand) but the main
>thing holding me up the different ways death and restarts is handled between
>the two. In AGT, a player can have a limited number of lives. Is there a way

>to keep track of how many times the player has died in TADS, or the game
>restarted, or some other way to handle this? Keeping track of the initial

Yeah. The restart function can optionally take two arguments, a
function and an argument to the function, the former being called with
the latter *after* the restart occurs (look at restartVerb's code in
adv.t for an example)

So if you're using global.numDeaths to keep track of the number of
deaths, I guess the right way to do things in die() is

die: function
{
if (global.numDeaths++ > 3)
{
"Too many deaths! Bye!";
quit();
}
else
{
"ok, I'll try'n resurrect you.\n";
restart(resetDeaths, global.numDeaths);
}
}

resetDeaths: function(d)
{
global.numDeaths := d;
}

And I think that should do it.

(Didn't some adventure scatter your possessions around when you died?
You could even simulate that with this method, by passing the function
a list of what you've got in your inventory)

>Wildman, the Cuberstalker
--
Dan Shiovitz || d...@cs.wisc.edu || http://www.cs.wisc.edu/~dbs
"...Incensed by some crack he had made about modern enlightened
thought, modern enlightened thought being practically a personal buddy
of hers, Florence gave him the swift heave-ho and--much against my
will, but she seemed to wish it--became betrothed to me." - PGW, J.a.t.F.S.

Wildman, the Cuberstalker

unread,
Nov 6, 1998, 3:00:00 AM11/6/98
to
On Fri, 6 Nov 1998 19:11:41 GMT, Andrew Plotkin <erky...@netcom.com> wrote:
>Wildman, the Cuberstalker (wil...@microserve.net) wrote:
>> >
>> >> Is there a way
>> >> to keep track of how many times the player has died in TADS, or the game
>> >> restarted, or some other way to handle this? Keeping track of the initial
>> >> state of all objects, then resetting them (except for the lives counter)
>> >> without using restart() is rather impractical.
>
>And yet, a surprising number of people have done it. :) TADS or Inform,
>but the idea is the same -- for every property of an object that might
>change, including location, keep a backup property, and a write a routine
>to restore them all.
>
>It's a pain in the ass, but a simple concept nevertheless.

Indeed, the brute force method does the job. I was hoping for something more
elegant though. Considering my options - write/read an external file or use
backup properties - I might just leave this feature out.

David Glasser

unread,
Nov 6, 1998, 3:00:00 AM11/6/98
to
Wildman, the Cuberstalker <wil...@microserve.net> wrote:

> I am in the process of writing a perl script that converts AGT games
> (decompiled with agtout) into TADS source files. The script can't handle
> every situation (metacommands need to be converted by hand) but the main
> thing holding me up the different ways death and restarts is handled between

> the two. In AGT, a player can have a limited number of lives. Is there a way


> to keep track of how many times the player has died in TADS, or the game
> restarted, or some other way to handle this? Keeping track of the initial
> state of all objects, then resetting them (except for the lives counter)
> without using restart() is rather impractical.
>

> FWIW I was doing a MiSTing of 'Space Aliens Laughed at my Cardigan'. I
> decided I wanted to work with a straight port. I soon realized that doing it
> by hand was a major pain. I eventually got carried away. :)

modify restartVerb
restartGame(actor) =
{
local yesno;
while ( true )
{
"Are you sure you want to start over? (YES or NO) > ";
yesno := yorn();
if ( yesno = 1 )
{
"\n";
scoreStatus(0, 0);
restart(); //got rid of the call to initRestart
break;
}
else if ( yesno = 0 )
{
"\nOkay.\n";
break;
}
}
}
;

modify global
dieCount = 0
;

dieCounter : function;

Change die() in std.t (possibly using replace after std's inclusion):

die: function //probably replace
{
"\b*** You have died ***\b";
scoreRank();
"\bYou may restore a saved game, start over, quit, or undo
the current command.\n";
while ( 1 )
{
local resp;

"\nPlease enter RESTORE, RESTART, QUIT, or UNDO: >";
resp := upper(input());
if ( resp = 'RESTORE' )
{
resp := askfile( 'File to restore' );
if ( resp = nil ) "Restore failed. ";
else if ( restore( resp )) "Restore failed. ";
else
{
Me.location.lookAround(true);
scoreStatus( global.score, global.turnsofar );
abort;
}
}
else if ( resp = 'RESTART' )
{
scoreStatus( 0, 0 );
//changed the next line
restart(dieCounter, global.diecount);
}
else if ( resp = 'QUIT' )
{
terminate();
quit();
abort;
}
else if (resp = 'UNDO')
{
if (undo())
{
"(Undoing one command)\b";
Me.location.lookAround(true);
scoreStatus(global.score, global.turnsofar);
abort;
}
else
"Sorry, no undo information is available. ";
}
}
}

Add in:

dieCounter : function(parm)
{
parm++;
global.diecount := parm;
//insert a check to see if diecount is too high
if (parm > 2)
"\(Silly fool! You're dead!\)\n";
}

This works (stick them at the end of goldskull.t and pick up the skull a
few times).

It only traps post-mortem restarts, not normal restarts, restores, or
undos. If you want to trap normal restarts, go back to restartVerb (in
which I changed a restart(initRestart, global.initRestartParam) into a
restart()) and change that restart into restart(dieCounter,
global.diecount).

If you want to trap restores (and I think undos), there is an orthagonal
way. Check recent versions of the manual (maybe the 2.2 updates, or of
course the new consolidated manual).

--
David Glasser gla...@NOSPAMuscom.com http://onramp.uscom.com/~glasser
DGlasser @ ifMUD : fovea.retina.net 4000 (webpage fovea.retina.net:4001)
Sadie Hawkins, official band of David Glasser: http://sadie.retina.net
"We take our icons very seriously in this class."

Jonadab the Unsightly One

unread,
Nov 7, 1998, 3:00:00 AM11/7/98
to
erky...@netcom.com (Andrew Plotkin) wrote:

> And yet, a surprising number of people have done it. :) TADS or Inform,
> but the idea is the same -- for every property of an object that might
> change, including location, keep a backup property, and a write a routine
> to restore them all.
>
> It's a pain in the ass, but a simple concept nevertheless.

Shame there isn't a TADS equivalent of imem.h

Imagine:

for (i=1:i<=top_object:i++)
{
SwapOut(i);
SwapIn(i);
}

I think that would qualify as an unintended usefulness of imem.

- jonadab

Wildman, the Cuberstalker

unread,
Nov 7, 1998, 3:00:00 AM11/7/98
to
On 6 Nov 1998 22:03:30 GMT, Dan Shiovitz <d...@cs.wisc.edu> wrote:
>Yeah. The restart function can optionally take two arguments, a
>function and an argument to the function, the former being called with
>the latter *after* the restart occurs (look at restartVerb's code in
>adv.t for an example)
> restart(resetDeaths, global.numDeaths);

Interesting. I was replacing initRestart, and having no luck. So the second
argument does what? And do you need a seperate function, or can initRestart
be replaced and called with:
restart(initRestart,global.lives);
?

Thanks, and also thanks to David Glasser for his simular (and more verbose)
solution.

David Glasser

unread,
Nov 7, 1998, 3:00:00 AM11/7/98
to
Wildman, the Cuberstalker <wil...@microserve.net> wrote:

> On 6 Nov 1998 22:03:30 GMT, Dan Shiovitz <d...@cs.wisc.edu> wrote:
> >Yeah. The restart function can optionally take two arguments, a
> >function and an argument to the function, the former being called with
> >the latter *after* the restart occurs (look at restartVerb's code in
> >adv.t for an example)
> > restart(resetDeaths, global.numDeaths);
>
> Interesting. I was replacing initRestart, and having no luck. So the second
> argument does what? And do you need a seperate function, or can initRestart
> be replaced and called with:
> restart(initRestart,global.lives);
> ?
>
> Thanks, and also thanks to David Glasser for his simular (and more verbose)
> solution.

initRestart is just the name of the sample, nearly pointless, function
to be called at restart time.

Basically, when one does restart(someFunction, someExpression), it
restarts the game, but before doing anything (like init()), it calls
someFunction with the value of someExpression. So, basically, the
second argument to restart is a way to save a piece of data (which could
even be a list) from being destroyed between restarts.

By the way, my version (unlike Dan's) didn't automatically restart at
death, giving the normal menu. Dunno which one you want.

Andrew Plotkin

unread,
Nov 7, 1998, 3:00:00 AM11/7/98
to
David Glasser (gla...@DELETEuscom.com) wrote:
> initRestart is just the name of the sample, nearly pointless, function
> to be called at restart time.
>
> Basically, when one does restart(someFunction, someExpression), it
> restarts the game, but before doing anything (like init()), it calls
> someFunction with the value of someExpression.

Neat idea.

Note for the next IF VM (amazingly, an idea that I *don't* think should be
moved out into the I/O module. :-)... the opcodes that restore memory
should take optional parameters to *protect* a block of memory. "Restore
memory from this file, except for addresses 100 to 120." Ditto for the
restart opcode.

(I'm generally against callback procedures in a VM; they complicate the
interpreter and are almost impossible to specify clearly. But you can
easily simulate the TADS callback effect with this mechanism: protect one
byte, store a flag in it, and put "if flag call..." at the beginning of
your game and after the save opcode.)

Jonadab the Unsightly One

unread,
Nov 8, 1998, 3:00:00 AM11/8/98
to
erky...@netcom.com (Andrew Plotkin) wrote:

> the opcodes that restore memory
> should take optional parameters to *protect* a block of memory. "Restore
> memory from this file, except for addresses 100 to 120." Ditto for the
> restart opcode.

I vote for that. All manner of possibilities, from simple to
complex, arise with it. Particularly if UNDO is included, too.
Then you can keep track of the number of successive UNDOs
(assuming the interpreter sets the 'multi-undo available' bit), keep
the player from getting through the minefield (trace the one raif
thread back a little ways -- you'll find it) without the map, or
whatever. You could even annoy the player after every successful
undo. ("Say, twelve UNDOs in succession -- isn't that a bit much?
One of these days I've got to get an adventurer that doesn't
use UNDO so much. Ah, well. Very well. Previous turn. Try
doing something *right* for a change now, eh?")

Heh, heh, heh.

- jonadab

Wildman, the Cuberstalker

unread,
Nov 8, 1998, 3:00:00 AM11/8/98
to
On Sat, 7 Nov 1998 15:20:18 -0500, David Glasser <gla...@DELETEuscom.com>

wrote:
>initRestart is just the name of the sample, nearly pointless, function
>to be called at restart time.
>
>Basically, when one does restart(someFunction, someExpression), it
>restarts the game, but before doing anything (like init()), it calls
>someFunction with the value of someExpression. So, basically, the
>second argument to restart is a way to save a piece of data (which could
>even be a list) from being destroyed between restarts.
>
>By the way, my version (unlike Dan's) didn't automatically restart at
>death, giving the normal menu. Dunno which one you want.

Well, all I really needed was that bit about restart(foo,bar). I'm really
surprised I wasn't able to find it in the manual, although I'll admit I
didn't look too hard.

To be sure I understand this right...

agtrestart: function(parm)
{
--global.lives;
}

then in die()
restart(agtrestart,global.lives);

When global.lives reaches 0, death is final. Or would it be better to do...

agtrestart: function(parm)
{
global.lives := parm;
}

then in die()
--global.lives;
restart(agtrestart,global.lives);


I can't test it just yet, as the script doesn't produce a compilable file
yet. (It did yesterday, but I've added "Yet Another Feature" and I broke
it again.)
Thanks again.

David Glasser

unread,
Nov 8, 1998, 3:00:00 AM11/8/98
to
Wildman, the Cuberstalker <wil...@microserve.net> wrote:

> Well, all I really needed was that bit about restart(foo,bar). I'm really
> surprised I wasn't able to find it in the manual, although I'll admit I
> didn't look too hard.
>
> To be sure I understand this right...
>
> agtrestart: function(parm)
> {
> --global.lives;
> }
>
> then in die()
> restart(agtrestart,global.lives);

This does not work. Think about it. The interpreter sees the restart
function. It puts aside in a special corner the function (name,
reference, whatever) agtrestart, and the value of global.lives (say,
three). Then, it wipes out everything else: including global.lives.

It then calls agtrestart(3). Your function then subtracts one from
global.lives, *completely ignoring* the parameter.

> When global.lives reaches 0, death is final. Or would it be better to do...
>
> agtrestart: function(parm)
> {
> global.lives := parm;
> }
>
> then in die()
> --global.lives;
> restart(agtrestart,global.lives);

This would work. In die, you'd want to check if global.lives is 0, and
die if so. This check would go before the decrement if global.lives
represents the amount of restarts possible (i.e., an original value of
g.l = 1 means one death restart allowed), or between the decrement and
restart() if g.l represents lifetimes (g.l = 1 means no restart).

Of course, all that what you wrote pays attention to is restarts after
death, not restores, undos, or explicitly typed-in restarts. Dunno if
this is what you want.

> I can't test it just yet, as the script doesn't produce a compilable file
> yet. (It did yesterday, but I've added "Yet Another Feature" and I broke
> it again.)
> Thanks again.

This sounds like a great program. If it is really solid, I could play
some of those AGT games on the archive on my Mac easily!

Wildman, the Cuberstalker

unread,
Nov 9, 1998, 3:00:00 AM11/9/98
to
On Sun, 8 Nov 1998 21:42:25 -0500, David Glasser <gla...@DELETEuscom.com>
wrote:

>This does not work. Think about it. The interpreter sees the restart
>function. It puts aside in a special corner the function (name,
>reference, whatever) agtrestart, and the value of global.lives (say,
>three). Then, it wipes out everything else: including global.lives.
>
>It then calls agtrestart(3). Your function then subtracts one from
>global.lives, *completely ignoring* the parameter.

It *does* work. I finally fixed some errors and tested it. It works as
expected. Completely ignoring that parameter may be "bad code", but it
doesn't affect how it functions.

>This would work. In die, you'd want to check if global.lives is 0, and
>die if so. This check would go before the decrement if global.lives
>represents the amount of restarts possible (i.e., an original value of
>g.l = 1 means one death restart allowed), or between the decrement and
>restart() if g.l represents lifetimes (g.l = 1 means no restart).

Indeed.

>Of course, all that what you wrote pays attention to is restarts after
>death, not restores, undos, or explicitly typed-in restarts. Dunno if
>this is what you want.

AGT doesn't support undo or restart (although AGiliTy does, as well as
altering a lot of other things) so I've disabled them. As for restores, I
haven't tested that yet.

>This sounds like a great program. If it is really solid, I could play
>some of those AGT games on the archive on my Mac easily!

It works on the output of agtout, with the m parameter (to get the
messages.) You lose the title and instructions since these reside in their
own files, and agtout won't extract them even from AGX files. (I'm
considering reading in those files though.) Auto-converting metacommands may
be possible, but I haven't considered supporting that yet. However, some
games that were merely compiled GAGS games should convert easily. It's
purpose isn't to make games more portable (AGiliTy does that) but to convert
games to a superior format for enhancement.

David Glasser

unread,
Nov 9, 1998, 3:00:00 AM11/9/98
to
Wildman, the Cuberstalker <wil...@microserve.net> wrote:

> On Sun, 8 Nov 1998 21:42:25 -0500, David Glasser <gla...@DELETEuscom.com>
> wrote:
> >This does not work. Think about it. The interpreter sees the restart
> >function. It puts aside in a special corner the function (name,
> >reference, whatever) agtrestart, and the value of global.lives (say,
> >three). Then, it wipes out everything else: including global.lives.
> >
> >It then calls agtrestart(3). Your function then subtracts one from
> >global.lives, *completely ignoring* the parameter.
>
> It *does* work. I finally fixed some errors and tested it. It works as
> expected. Completely ignoring that parameter may be "bad code", but it
> doesn't affect how it functions.

Are you sure? Unless I'm completely confused, this should only work if
there is only one restart allowed.

Wildman, the Cuberstalker

unread,
Nov 10, 1998, 3:00:00 AM11/10/98
to
On Mon, 9 Nov 1998 21:17:56 -0500, David Glasser <gla...@DELETEuscom.com>

wrote:
>Wildman, the Cuberstalker <wil...@microserve.net> wrote:
>
>> On Sun, 8 Nov 1998 21:42:25 -0500, David Glasser <gla...@DELETEuscom.com>
>> wrote:
>> >This does not work. Think about it. The interpreter sees the restart
>> >function. It puts aside in a special corner the function (name,
>> >reference, whatever) agtrestart, and the value of global.lives (say,
>> >three). Then, it wipes out everything else: including global.lives.
>> >
>> >It then calls agtrestart(3). Your function then subtracts one from
>> >global.lives, *completely ignoring* the parameter.
>>
>> It *does* work. I finally fixed some errors and tested it. It works as
>> expected. Completely ignoring that parameter may be "bad code", but it
>> doesn't affect how it functions.
>
>Are you sure? Unless I'm completely confused, this should only work if
>there is only one restart allowed.

You are correct, sir! I added one more life and discovered why that was bad.
The way you suggested is what I'm using now. Thanks.

TenthStone

unread,
Nov 10, 1998, 3:00:00 AM11/10/98
to
gla...@DELETEuscom.com (David Glasser) caused this to appear in our
collective minds on Mon, 9 Nov 1998 21:17:56 -0500:

>Wildman, the Cuberstalker <wil...@microserve.net> wrote:
>
>> On Sun, 8 Nov 1998 21:42:25 -0500, David Glasser <gla...@DELETEuscom.com>
>> wrote:
>> >This does not work. Think about it. The interpreter sees the restart
>> >function. It puts aside in a special corner the function (name,
>> >reference, whatever) agtrestart, and the value of global.lives (say,
>> >three). Then, it wipes out everything else: including global.lives.
>> >
>> >It then calls agtrestart(3). Your function then subtracts one from
>> >global.lives, *completely ignoring* the parameter.
>>
>> It *does* work. I finally fixed some errors and tested it. It works as
>> expected. Completely ignoring that parameter may be "bad code", but it
>> doesn't affect how it functions.
>
>Are you sure? Unless I'm completely confused, this should only work if
>there is only one restart allowed.

I think you're right. After restart, global.lives should be equal to the
default value, 3. Each successive restart would set global.lives back to
three, and then the function would decrement global.lives. Thus,
global.lives would be 3 upon startup and 2 ever after the first death.

By the way, the same routine is allowed with restore(), so make sure both
your bases are convered.

-----------

The imperturbable TenthStone
tenth...@hotmail.com mcc...@erols.com mcc...@gsgis.k12.va.us

Reply all
Reply to author
Forward
0 new messages