Saturday, March 23, 2013
3.1
Just letting you all know that Roodylib v3.1 is now up on Hugo by Example and the bitbucket repository. I also uploaded it to the IF Archive, but it'll probably be some days before that one is accepted.
straight from the Hugo source
So I asked Kent Tessman about exactly what was going wrong when something like my ClearArray routine uses up all of the UNDO memory. He answered:
Anyhow, I'll try to have a new version of Roodylib up soon.
The way Hugo's undo currently works, it stores a stack of individual commands, and basically sticks a bookmark in where a player command would be. Sorry, but the first 'individual commands' I mean 'program statements' or 'program commands'. So when you assign a variable or -- as you discovered, a single array element -- it uses an undo slot. Now, there are lots of them, but the default (and essentially at this point standard) engine has an arbitrary limit to keep memory usage down.So that'll be an interesting thing to keep in mind in the future. I then asked him if, memory-wise, array elements, variables, and property elements- and the setting of- are interchangeable. He answered:
For the most part. I mean, basically what Hugo does is allocate a big chunk of bytes, and divvies it up.So I think the lesson to be learned from that is to not put all of your state-tracking changes in one basket but to split them up between arrays, property arrays, and variables. Whatever the case, most importantly, don't write code that sets many of these values needlessly.
Anyhow, I'll try to have a new version of Roodylib up soon.
Friday, March 22, 2013
"new fuse" progress
I really was liking this new fuse system, but the fact that using it seemed to limit even a small game to one UNDO was killing me. As far as I could tell, the problem was my call to "runevents" after an UNDO and other xverb commands. It just was taking up too much memory.
I decided just now that I'd code my own version of "runevents" that I'd run in such cases. I call it... fake_runevents!
The new "NEW_FUSE" code:
Anyhow, my guess is that "runevents" uses a fair amount of memory since it's doing a lot of FindObject-y stuff behind the scenes, and this method of helping Hugo find the fuses and daemons quicker seems to help my test game out a good amount.
I decided just now that I'd code my own version of "runevents" that I'd run in such cases. I call it... fake_runevents!
The new "NEW_FUSE" code:
#ifclear NO_FUSESSo, you copy your "event in <fuse or daemon>" code to the fuse's fake_event property. Of course, like the old "new fuse" stuff, whether or not your code properly prints stuff will be based on whether your code uses the new counter-based timing effectively.
#ifset NEW_FUSE
property fuse_length alias e_to
replace fuse
{
type fuse
size 0
timer
{
if self.fuse_length
return (self.fuse_length - counter)
else
return 0
}
fuse_length 0
in_scope 0
tick
{
local a
a = self.timer
if a <= 0
self.fuse_length = 0
#ifset DEBUG
if debug_flags & D_FUSES
{
print "[Running fuse "; number self; ": timer = ";
print number a; "]"
}
#endif
if a = 0
Deactivate(self)
return a
}
}
property timer_start alias fuse_length
property fake_event alias s_to
replace daemon
{
type daemon
size 0
in_scope 0
timer
{
if self.timer_start
{
return (counter - self.timer_start)
}
else
return 0
}
}
object fuse_bucket
{}
routine fake_runevents
{
local i
for i in fuse_bucket
{
! if i.type = fuse
! {
run i.fake_event
! }
}
}
#endif ! NEW_FUSE
!\
Activate - added a warning for when it is called before the player global has been set.
\!
replace Activate(a, set) ! <set> is for fuses only
{
local err
if not player
{
Font(BOLD_ON)
print "[WARNING: The player global must be set before
daemon (object "; number a;") can be activated.]"
err = true
}
#ifset NEW_FUSE
move a to fuse_bucket
#endif
a.in_scope = player
a is active
if a.type = fuse and not err
{
if set
#ifclear NEW_FUSE
a.timer = set
#else
a.fuse_length = (counter + set)
#endif
run a.activate_event
}
elseif a.type = daemon and not err
{
#ifclear NEW_FUSE
if set and not a.#timer
#else
if set and not a.#timer_start
#endif
{
Font(BOLD_ON)
print "[WARNING: Attempt to set nonexistent timer
property on daemon (object "; number a; ")]"
err = true
}
else
#ifclear NEW_FUSE
a.timer = set
#else
a.timer_start = (counter - set)
#endif
run a.activate_event
}
elseif not err
{
Font(BOLD_ON)
print "[WARNING: Attempt to activate non-fuse/\
daemon (object "; number a; ")]"
err = true
}
#ifset DEBUG
if debug_flags & D_FUSES and not err
{
print "[Activating "; a.name; " "; number a;
if a.type = fuse
print " (timer = "; number a.timer; ")";
print "]"
}
#endif
if err
{
Font(BOLD_OFF)
"\npress a key to continue..."
HiddenPause
}
return (not err)
}
replace Deactivate(a)
{
local err
remove a
a.in_scope = 0
a is not active
if a.type ~= fuse and a.type ~= daemon
{
print "[WARNING: Attempt to deactivate non-fuse/\
daemon (object "; number a; ")]"
err = true
}
else
{
run a.deactivate_event
}
#ifset DEBUG
if debug_flags & D_FUSES and not err
{
print "[Deactivating "; a.name; " "; number a; "]"
}
#endif
return (not err)
}
#endif ! ifclear NO_FUSES
Anyhow, my guess is that "runevents" uses a fair amount of memory since it's doing a lot of FindObject-y stuff behind the scenes, and this method of helping Hugo find the fuses and daemons quicker seems to help my test game out a good amount.
more about what went wrong
So, let's share an example of memory-hogging code. This is the ClearArray routine that was sucking a lot of available memory out of Roodylib-using games:
The problem was that I was using this with _temp_string every turn, and _temp_string has 256 elements, meaning that it goes through the loop 256 times. This all seems to count against the available memory.
The reason I wrote this routine in the first place was because of an instance in The Clockwork Boy 2 was because something was overwriting the end 0 bit of the current string, so the game kept printing until it got to the end of a previously-saved string. I thought, a-ha, I'll fix this by clearing _temp_string all of the time!
Of course, now I wish I had just fixed whatever was writing over that 0 bit. I can't remember what the code was like before, but in the meantime, I changed my PrintStatusLine code to not use ClearArray.
Instead of throwing ClearArray out altogether (although I might do it eventually anyway), I changed the loop to quit out as soon as the array has two empty elements in a row:
The main thing is, let this be a good lesson to not let your code loop more than it has to.
routine ClearArray(array_to_be_cleared)
{
local n,t
for (n=0;n< array array_to_be_cleared[] ; n++ )
{
array array_to_be_cleared[n] = 0
}
}
The problem was that I was using this with _temp_string every turn, and _temp_string has 256 elements, meaning that it goes through the loop 256 times. This all seems to count against the available memory.
The reason I wrote this routine in the first place was because of an instance in The Clockwork Boy 2 was because something was overwriting the end 0 bit of the current string, so the game kept printing until it got to the end of a previously-saved string. I thought, a-ha, I'll fix this by clearing _temp_string all of the time!
Of course, now I wish I had just fixed whatever was writing over that 0 bit. I can't remember what the code was like before, but in the meantime, I changed my PrintStatusLine code to not use ClearArray.
Instead of throwing ClearArray out altogether (although I might do it eventually anyway), I changed the loop to quit out as soon as the array has two empty elements in a row:
routine ClearArray(array_to_be_cleared)
{
local n,t
for (n=0;n< array array_to_be_cleared[] ; n++ )
{
if array array_to_be_cleared[n] = 0
t++
else
{
array array_to_be_cleared[n] = 0
t = 0
}
if t = 2
{
break
}
}
}
The main thing is, let this be a good lesson to not let your code loop more than it has to.
Thursday, March 21, 2013
worst case scenario
Grrr, so annoyed. While testing out that fuses code today, I noticed that games compiled with recent versions of Roodylib don't have enough memory for even one UNDO. Truth be told, I often test new code with Gargoyle since the Gargoyle window is so unobtrusive, but Gargoyle's UNDO buffer is so small that I probably didn't blink twice when newly compiled games were unable to compile.
Testing now with Hugor, Roodylib has been short on UNDO memory since before I started uploading to bitbucket (and definitely before the 3.0 release I uploaded to the IF Archive).
The good news is, getting rid of just one for loop called by PrintStatusLine freed up space for one UNDO, and compiling without my new fuse code frees up memory for even more.
The bad news is, now I need to go through all of Roodylib with a particular eye for optimization. Having games unable to UNDO is unacceptable. I've long been worried that I'd run into this problem, but it is quite annoying to have to deal with it.
My optimization strategy will consist of cutting any loops that I can and making sure that the others are as concise as possible. Beyond that, I've long been suspicious that Perform doesn't need to call SetUpDirectionObjects every time, and I think I'll see if I can move it somewhere else.
Testing now with Hugor, Roodylib has been short on UNDO memory since before I started uploading to bitbucket (and definitely before the 3.0 release I uploaded to the IF Archive).
The good news is, getting rid of just one for loop called by PrintStatusLine freed up space for one UNDO, and compiling without my new fuse code frees up memory for even more.
The bad news is, now I need to go through all of Roodylib with a particular eye for optimization. Having games unable to UNDO is unacceptable. I've long been worried that I'd run into this problem, but it is quite annoying to have to deal with it.
My optimization strategy will consist of cutting any loops that I can and making sure that the others are as concise as possible. Beyond that, I've long been suspicious that Perform doesn't need to call SetUpDirectionObjects every time, and I think I'll see if I can move it somewhere else.
fairly fuse-ful
One of my dissatisfactions with the current state of Roodylib is that things like redrawing the screen after a screen size change or after leaving a menu does not include relevant event code if something happens to be going on. Luckily, this doesn't come up very often, and it's probably pretty easy to shrug off but it still annoys me.
You could just call runevents again, but since most fuses and daemons are timer-based, many would progress an extra turn where you do not want them to. When I used to think about how I could conceivably fix this, I thought about a complicated write-event-messages-to-string-arrays system that'd print messages again. Today, it occurred to me to change the fuse code to redefine how they work. In my new code, the timer now is a calculation between the game counter and fuse length.
While not necessary for the code to work, I added a new property to fuse objects so existing fuse code out there will still work with the new stuff. Since daemon code (I mean that in the non-fuse sense) usage is so open-ended, I took a gamble and coded it to assume the opposite-of-fuse, a timer that increases with every turn instead of decreases.
Of course, depending on what your fuse/daemon code does, you'd still have to code them strategically so they can be smartly run several times per turn.
This is another thing where I'm not sure how quickly I'll add it to RoodyLib, as it may make some assumptions that might not be good for all games.
Anyhow, the code:
You could just call runevents again, but since most fuses and daemons are timer-based, many would progress an extra turn where you do not want them to. When I used to think about how I could conceivably fix this, I thought about a complicated write-event-messages-to-string-arrays system that'd print messages again. Today, it occurred to me to change the fuse code to redefine how they work. In my new code, the timer now is a calculation between the game counter and fuse length.
While not necessary for the code to work, I added a new property to fuse objects so existing fuse code out there will still work with the new stuff. Since daemon code (I mean that in the non-fuse sense) usage is so open-ended, I took a gamble and coded it to assume the opposite-of-fuse, a timer that increases with every turn instead of decreases.
Of course, depending on what your fuse/daemon code does, you'd still have to code them strategically so they can be smartly run several times per turn.
This is another thing where I'm not sure how quickly I'll add it to RoodyLib, as it may make some assumptions that might not be good for all games.
Anyhow, the code:
property fuse_length alias e_to
replace fuse
{
type fuse
size 0
timer
{
if self.fuse_length
return (self.fuse_length - counter)
else
return 0
}
fuse_length 0
in_scope 0
tick
{
if self.timer <= 0
self.fuse_length = 0
#ifset DEBUG
if debug_flags & D_FUSES
{
print "[Running fuse "; number self; ": timer = ";
print number self.timer; "]"
}
#endif
if self.timer = 0
Deactivate(self)
return self.timer
}
}
property timer_start alias fuse_length
replace daemon ! replaced just the daemon is near the fuse in the object tree
{
type daemon
size 0
in_scope 0
timer
{
if self.timer_start
{
return (counter - self.timer_start)
}
else
return 0
}
}
!\
Activate - added a warning for when it is called before the player global has been set.
\!
replace Activate(a, set) ! <set> is for fuses only
{
local err
if not player
{
Font(BOLD_ON)
print "[WARNING: The player global must be set before
daemon (object "; number a;") can be activated.]"
err = true
}
a.in_scope = player
a is active
if a.type = fuse and not err
{
if set
a.fuse_length = (counter + set)
run a.activate_event
}
elseif a.type = daemon and not err
{
if set and not a.#timer_start
{
Font(BOLD_ON)
print "[WARNING: Attempt to set nonexistent timer
property on daemon (object "; number a; ")]"
err = true
}
else
a.timer_start = (counter - set)
run a.activate_event
}
elseif not err
{
Font(BOLD_ON)
print "[WARNING: Attempt to activate non-fuse/\
daemon (object "; number a; ")]"
err = true
}
#ifset DEBUG
if debug_flags & D_FUSES and not err
{
print "[Activating "; a.name; " "; number a;
if a.type = fuse
print " (timer = "; number a.timer; ")";
print "]"
}
#endif
if err
{
Font(BOLD_OFF)
"\npress a key to continue..."
HiddenPause
}
return (not err)
}
replace Deactivate(a)
{
local err
remove a
a.in_scope = 0
a is not active
if a.type ~= fuse and a.type ~= daemon
{
print "[WARNING: Attempt to deactivate non-fuse/\
daemon (object "; number a; ")]"
err = true
}
else
{
run a.deactivate_event
}
#ifset DEBUG
if debug_flags & D_FUSES and not err
{
print "[Deactivating "; a.name; " "; number a; "]"
}
#endif
return (not err)
}
#endif ! ifclear NO_FUSES
Wednesday, March 20, 2013
Describe-re-Place
I have to admit it. I love "to do" lists. This is one of the reasons I'll miss iGoogle when it's gone, as it provides a "to do" list app for the unobtrusive Chrome home page I have set up. That list is great, but I also have to admit that some of my most effective "to do" lists are just a couple text files I have on my desktop.
I've been revisiting those desktop lists lately to make sure I've accomplished everything listed there. I was able to delete a good deal of what they listed, but at least one of them reminded me of an unsuccessful bid to redesign DescribePlace into something that'd allow the addition of extra bits of text, like something that'd call that CanGo routine that lists room exits.
I like running DescribePlace with the DESCFORM_F format mask which gives room contents a sort of "double spaced" look to it. The heart of the DescribePlace problem is that you really need to know when stuff will be printed and when it will not to properly space it all.
Even though I really had no intention to return to the problem any time soon, I happened to lie down for a late afternoon nap yesterday, and almost instantly, I was hit by a DescribePlace replacement idea. Instead of having it be one routine, I thought I'd give it a main routine that calls routines that does the actual object listing. Then we have the benefit of returning true or false to determine whether or not to print extra lines.
I spent some time today putting the idea to action, and it already seems to be in a usable state. I think I even fixed some problems with the old replacement which required some elements to be listed after certain other ones. I have a feeling some of the routines could be more efficient or cleaner-looking, but that's for another day.
What does this mean for the average author? Well, since they probably won't touch anything, it doesn't really mean anything. Everything should work the same, although there are also some fixes in there for when using-DescribePlace-to-describe-a-room-that-isn't-your-location.
Like the old replacement, it should be helpful for games that like to change up the order in which things are listed, like Robb's games where characters are sometimes listed last. To change the order, just add something like this to your init or SetGlobalsAndFillArrays routine:
Being so modular, it should also be easier to change the behavior of just how certain things are listed.
I also think that this will added to Roodylib, but I have to first decide if all of the new spacing behavior should fall under DESCFORM_F or if I need to come up with a new Infocom-spacing format mask called DESCFORM_I or something. I'll have to look at my old notes to see what I decided with that.
Anyhow, here's the code so far:
I've been revisiting those desktop lists lately to make sure I've accomplished everything listed there. I was able to delete a good deal of what they listed, but at least one of them reminded me of an unsuccessful bid to redesign DescribePlace into something that'd allow the addition of extra bits of text, like something that'd call that CanGo routine that lists room exits.
I like running DescribePlace with the DESCFORM_F format mask which gives room contents a sort of "double spaced" look to it. The heart of the DescribePlace problem is that you really need to know when stuff will be printed and when it will not to properly space it all.
Even though I really had no intention to return to the problem any time soon, I happened to lie down for a late afternoon nap yesterday, and almost instantly, I was hit by a DescribePlace replacement idea. Instead of having it be one routine, I thought I'd give it a main routine that calls routines that does the actual object listing. Then we have the benefit of returning true or false to determine whether or not to print extra lines.
I spent some time today putting the idea to action, and it already seems to be in a usable state. I think I even fixed some problems with the old replacement which required some elements to be listed after certain other ones. I have a feeling some of the routines could be more efficient or cleaner-looking, but that's for another day.
What does this mean for the average author? Well, since they probably won't touch anything, it doesn't really mean anything. Everything should work the same, although there are also some fixes in there for when using-DescribePlace-to-describe-a-room-that-isn't-your-location.
Like the old replacement, it should be helpful for games that like to change up the order in which things are listed, like Robb's games where characters are sometimes listed last. To change the order, just add something like this to your init or SetGlobalsAndFillArrays routine:
DescribePlaceArray[0] = &CharsWithoutDescs, &ObjsWithoutDescs,(Except in whatever order you want)
&ParentofPlayer, &CharsWithDescs, &ObjsWithDescs, &AttachablesScenery
Being so modular, it should also be easier to change the behavior of just how certain things are listed.
I also think that this will added to Roodylib, but I have to first decide if all of the new spacing behavior should fall under DESCFORM_F or if I need to come up with a new Infocom-spacing format mask called DESCFORM_I or something. I'll have to look at my old notes to see what I decided with that.
Anyhow, here's the code so far:
! This constant needs to be declared with something higher if you are
! adding additional elements into the mix
#if undefined DESCRIBEPLACE_ELEMENTS
constant DESCRIBEPLACE_ELEMENTS 6
#endif
attribute already_printed
array DescribePlaceArray[DESCRIBEPLACE_ELEMENTS]
replace Describeplace(place, long)
{
local obj, didprint
local need_carriage
parser_data[PARSER_STATUS] &= ~PRONOUNS_SET
! Since, for example, a room description following entering via
! DoGo does not trigger before/after properties tied to looking
! around:
!
#ifclear NO_VERBS
if verbroutine = &MovePlayer ! was "if verbroutine ~= &DoLookAround"
{
if place is not visited and verbosity ~= 1
return Perform(&DoLookAround)
elseif long = true or verbosity = 2
return Perform(&DoLookAround)
}
#endif
if not light_source
{
Message(&DescribePlace, 1) ! "It's too dark to see..."
return
}
place is known
! Print the name of the location as a heading
Font(BOLD_ON)
if not (FORMAT & DESCFORM_F)
print "\n";
print capital place.name;
! Print ", in <something>" if necessary
if location = place and player not in place
{
if parent(player).prep
print ", "; parent(player).prep; " ";
elseif parent(player) is platform
print ", "; ON_WORD; " ";
else
print ", "; IN_WORD; " ";
print Art(parent(player))
}
print newline
Font(BOLD_OFF)
override_indent = false
if place is not visited and verbosity ~= 1
{
if &place.initial_desc or &place.long_desc
{
didprint = true
Indent
}
if not place.initial_desc
run place.long_desc
}
elseif long = true or verbosity = 2
{
if &place.long_desc
{
Indent
didprint = true
}
run place.long_desc
}
elseif place is not visited and verbosity = 1
{
if &place.initial_desc
{
Indent
didprint = true
}
run place.initial_desc
}
if &place.list_contents and (FORMAT & DESCFORM_F) and didprint
print "" ! for double-space-after-heading formatting
! A location may contain an overriding listing routine, as may any
! parent, in the list_contents property
!
for obj in place
obj is not already_printed
if not place.list_contents
{
list_nest = 0
local a
while a < DescribePlaceArray[] and DescribePlaceArray[a]
{
need_carriage = call DescribePlaceArray[a](place)
if (FORMAT & DESCFORM_F)
{
if need_carriage and didprint
""
}
if need_carriage
{
need_carriage = false
didprint = call DescribePlaceArray[a](place,true)
}
a++
}
print newline
need_newline = false
}
}
! routine for listing the player's siblings when he or she is in or on an
! object in the room
routine ParentofPlayer(place, for_reals)
{
local obj
if not for_reals
{
if player not in place and place = location
{
for obj in (Parent(player))
{
if obj is not hidden and obj ~= player
return true
}
}
return false
}
else
{
list_nest = 1
if WhatsIn(Parent(player))
return true
}
}
! routine for listing characters with short_desc descriptions
routine CharsWithDescs(place, for_reals)
{
local obj
if not for_reals
{
for obj in place
{
if (obj is not hidden and obj is living and
obj ~= player and ((&obj.short_desc and verbosity ~= 1) or
(obj is not moved and &obj.initial_desc)) and
obj is not already_printed )
{
return true
}
}
return false
}
else
{
! List all characters, if any
! count = 0
for obj in place
{
if obj is hidden or obj is not living or
player in obj
{
obj is already_listed
}
else
{
obj is not already_listed
}
}
local ret
for obj in place
{
if obj is not already_listed
{
print newline
if verbosity ~= 1 or (obj is not moved and &obj.initial_desc)
ShortDescribe(obj)
if obj is already_listed ! did we print something?
{
ret = true
obj is already_printed
}
}
}
return ret
}
}
! routine for listing characters without short_desc descriptions
routine CharsWithoutDescs(place,for_reals)
{
local tempformat, count, obj
if not for_reals
{
for obj in place
{
if (obj is not hidden and obj is living and
obj ~= player and
(not &obj.short_desc or
(&obj.short_desc and verbosity = 1)) and
obj is not already_printed)
{
return true
}
}
return false
}
else
{
for obj in place
{
if (obj is not hidden and obj is living and
obj ~= player and
(not &obj.short_desc or
(&obj.short_desc and verbosity = 1)) and
obj is not already_printed)
{
obj is not already_listed
count++
}
else
obj is already_listed
}
if count ! list_count ! if characters are to be listed
{
list_count = count
tempformat = FORMAT
FORMAT = FORMAT | FIRSTCAPITAL_F | ISORAREHERE_F
FORMAT = FORMAT | USECHARNAMES_F
if (FORMAT & LIST_F)
{
FORMAT = FORMAT & ~LIST_F ! clear it
FORMAT = FORMAT | TEMPLIST_F
}
Indent
list_nest = 0
ListObjects(place)
FORMAT = tempformat
return true
}
return false
}
}
! routine for listing objects with short_desc descriptions
routine ObjsWithDescs(place, for_reals)
{
local obj, ret
if not for_reals
{
for obj in place
{
#ifset USE_ATTACHABLES
! Exclude all attachables for now (and characters)
if obj is not living and not obj.type = attachable and
player not in obj and obj is not hidden and
((verbosity ~= 1 and &obj.short_desc) or
(&obj.initial_desc and verbosity = 1)) and
obj is not already_printed
#else
if obj is not living and player not in obj and
obj is not hidden and
((verbosity ~= 1 and &obj.short_desc) or
(&obj.initial_desc and verbosity = 1)) and
obj is not already_printed
#endif
{
ret = true
obj is not already_listed
}
else
obj is already_listed
}
return ret
}
else
{
for obj in place
{
#ifset USE_PLURAL_OBJECTS
! ...And don't list identical objects yet, either
if (obj.identical_to).type = identical_class
{
if obj is not hidden
count++
}
elseif player not in obj
#else
if player not in obj
#endif
{
if obj is not already_listed and
obj is not hidden and obj is not already_printed
{
if verbosity ~= 1 or (verbosity = 1 and
(obj is not moved and &obj.initial_desc))
ShortDescribe(obj)
if obj is already_listed ! did we print something?
{
ret = true
obj is already_printed
}
}
}
}
return ret
}
}
! routine for listing objects without short_desc descriptions
routine ObjsWithoutDescs(place, for_reals)
{
local obj, tempformat, count
if not for_reals
{
for obj in place
{
#ifset USE_ATTACHABLES
! Exclude all attachables for now (and characters)
if obj is not living and not obj.type = attachable and
player not in obj and obj is not hidden and
obj is not already_printed and
(not &obj.short_desc or (&obj.short_desc and verbosity = 1))
#else
if obj is not living and player not in obj and
obj is not hidden and obj is not already_printed and
(not &obj.short_desc or (&obj.short_desc and verbosity = 1))
#endif
{
return true
}
}
return false
}
else
{
for obj in place
{
#ifset USE_ATTACHABLES
! Exclude all attachables for now (and characters)
if obj is living or obj.type = attachable or
player in obj or (&obj.short_desc and verbosity ~= 1) or
obj is already_printed
#else
if obj is living or player in obj or (&obj.short_desc and verbosity ~= 1) or obj is already_printed
#endif
obj is already_listed
else
{
count++
obj is not already_listed
}
}
if count
{
list_count = count
tempformat = FORMAT
FORMAT = FORMAT | FIRSTCAPITAL_F | ISORAREHERE_F
if FORMAT & LIST_F
{
FORMAT = FORMAT & ~LIST_F ! clear it
FORMAT = FORMAT | TEMPLIST_F
}
Indent
list_nest = 0
ListObjects(place)
FORMAT = tempformat
return true
}
return false
}
}
! routine for listing attachables and contents of scenery objects
routine AttachablesScenery(place, for_reals)
{
local obj, ret
if not for_reals
{
#ifset USE_ATTACHABLES
for obj in place
{
! Print attachables last
if obj.type = attachable and obj is not hidden
{
return true
}
}
#endif
for obj in place
{
if obj.type = scenery
{
if player not in obj and
(obj is open or obj is not openable)
{
local a
for a in obj
{
if a is not hidden
{
return true
}
}
}
}
}
return false
}
else
{
#ifclear NO_OBJLIB
#ifset USE_ATTACHABLES
for obj in place
{
! Print attachables last
if obj.type = attachable and obj is not hidden
{
ShortDescribe(obj)
if obj is not already_listed
Message(&DescribePlace, 2, obj)
obj = already_printed
ret = true
}
}
#endif
print newline
override_indent = false
! Finally, list contents of scenery objects (unless we've
! already done so as the parent of the player)
!
for obj in place
{
if obj.type = scenery
{
obj is known
if player not in obj and
(obj is open or obj is not openable)
{
list_nest = 1
if WhatsIn(obj)
ret = true
}
}
! For scenery-derived objects that may change the type
elseif obj is static, hidden
obj is known
}
return ret
#endif ! ifclear NO_OBJLIB
}
}
Subscribe to:
Posts (Atom)