Looping through all things of a specific class

How is this accomplished? I found this example in the documentation:

	local obj;  
	foreach(obj in gPlayerChar.contents)  
	{  
	   if(obj.ofKind(LightSource))  
	      break;  
	}  

But this assumes that I know what “gPlayerChar.contents” is. What if the task necessary is to loop through all rooms that exist in the game… I would be able to write this (with the obvious psuedo-code being the focus of my question):

	local obj;  
	foreach(obj in ?theEntireGame.???)  
	{  
	   if(obj.ofKind(Room))  
	      foo;  
	}  

I am just kind of lost and aimlessly pecking around in the library reference document and technical manual, but I am unsure how I should go about finding out what the different scopes might be. Can anyone answer this in a “teach a man to fish” kind of way, so that I can figure out how to loop through various scopes and for various kinds of things by finding that information in the documentation myself in the future?

gPlayerChar is just a macro expanding to currently active player character, which in games with only one player character is usually “me” object (not a rule, but I think almost all examples declare player character as “me”) so you could just write me.contents. Contents property is a list of objects that player character directly holds. This property is the central piece of containment model in TADS. Many relations between objects are modeled as a containment, be it objects the player holds or wears, rooms in which objects and players are currently located, or even more abstract relationships as actor states in an actor or topics in an actor state and so on. Thing and all derived classes have contents list and location counterpart which constitutes a tree of hierarchy.

Because containment is so core principle in TADS, setting these properties is usually accomplished by the handy plus syntactic sugar introduced in early chapters of Learning TADS3, but there are more details in a chapter about containment.

If you want to iterate over really all objects in a game, you could use firstObj/nextObj functions which you could use with or without concrete class:

local obj = firstObj(Book); while(obj != nil) { if(obj.author==gPlayerChar) obj.hasBeenRead = true; else obj.hasBeenRead = nil; obj = nextObj(obj, Book); }
But there is also a handy shortcut:

forEachInstance(Actor, {obj: obj.isAnimate = true});

Maybe you are trying to torture TADS and test some more advanced stuff without trying something simpler first to gain some solid basics. I would suggest to at least quickly read through Learning TADS3 systematically from begin to end to know what’s there and how problems in TADS are typically approached.

To iterate over every object in the game of class cls you’d typically do something like the following:

for(local obj = firstObj(cls); obj != nil; obj = nextObj(obj, cls))
{
   /* Do whatever you need to do with obj */
}

Thank you! Knowing search terms is always the problem starting out in a new language. I tried searching for “loop through things”, “for loop classes”, etc. along with the “tads” term, and just couldn’t find the answer.

Now that I see this, it makes sense, but it has me worried for performance, and the note about the garbage collector in the documentation such that using firstObj(cls) has the tendency to put objects back into accessible state, not allowing them to be deleted.

I was wondering if it might make sense to modify the classes themselves to add any object created from that class to a list, and then iterate over that list of items only when looking for items of that class? If this was done, and an item that was on that list became unused (except for being on that list) would the garbage collector remove it from the list appropriately?

Edit: I think I’ve had some success with this, although I’m likely going about it in a backwards, novice sort of way, but here is some code example of what I mean by the above:

roomList : Thing
    theList = [];

;

modify Room
    initializeThing ( ) {
        inherited();
        roomList.theList += self;
    }
;

myInitObj: InitObject
   execute()
   {
        for(local r = 1; r <= roomList.theList.length; r++) {
            say(roomList.theList[r].name);
        }
}
;

gameMain: GameMainDef
    initialPlayerChar = me
;

startRoom: Room 'Start Room'
    "This is the starting room."
;

+ me: Actor;

anotherRoom: Room 'another Room'
;

So, is this a bad idea for any reason? Will it improve/hurt performance at compile time? During play? Is there a better way to do essentially what I have outlined here? Is this bad for the garbage collector?

But when you add an object into the list, then the object is not unreferenced anymore and thus won’t be purged by GC.

The tendency to put objects into accessible state is IMHO not exactly as I would understand the manual. I would say it has tendency to visit otherwise unreferenced object which sits and waits to be deleted by GC, but if you are fine with that and you won’t make a new reference during visiting objects with firstObj/nextObj (or only temporarily), than it shouldn’t prevent them to be deleted when GC runs.

But of course periodically looping through all objects in game is performance-heavy.

Ok, to follow up on this, I’m trying my best as someone new to TADS to set up a way to capture all things in class lists. I have tried the code below as the first step. I just have the always true conditional there to remind me to do a check later (once I figure out this step first) to make sure to only create the list if it doesn’t exist yet. For now, I’m just trying to see if wiring things up this way even works at a high level.

I’m getting “nil” as the value for every getSuperClassList of every thing here though, and I don’t know why.

class kindList: Thing
    construct(name_)
    {
        name = name_;
        theList = [];
    }
;

modify Thing
    initializeThing() {
        inherited();
        if(1 == 1) {
            local kList = new kindList(toString(self.getSuperClassList()) + 'List');
            kList.theList += self;
            say(kList.name);
        }
    }
;

Probably because TADS 3 is case-sensitive and the method you want is called getSuperclassList not getSuperClassList.

Wow… thanks! I guess I would have expected it to throw a compiler error if I was calling something non-existent. Thanks again for finding that.

It should have thrown a compiler warning, but sometimes it’s easy to miss them.