Using properties as variables in a class

I’ve been playing around creating things and getting my head around how TADS works and methods for cutting down code. I’ve been trying to create a clothing class that won’t let you put two of the same type on, such as 2 hats or 2 pairs of shoes. I figured it out upto consilidating it down to one class ‘clothes’ rather than having ‘tops’, ‘pants’, ‘shoes’ etc and ended up with this where clothingType = gActor.wearingXXX

[spoiler][code]Clothes: Wearable
isListedInContents=(moved)
clothingType = nil
message = nil
dobjFor(Doff)
{
action()
{
clothingType = nil;
inherited;
}
}

dobjFor(Wear)
{ 
 verify() 
    {
        if(self.isWorn)
        { inherited; }
        
        else if(clothingType)
        {
            illogicalAlready('You\'re already wearing <<message>>, you don\'t need to wear any more');
        }
    }
 action()
    {
        clothingType = true;
        inherited; 
    }
}

;[/code][/spoiler]

I know that where it calls (clothingType) it actually points where it should since testing for if(!clothingType) makes it impossible to wear anything. I’m just a bit stumped as to why it isn’t working the same as when it was with gActor.wearingXXX in its place. Can I do it this way or is there some sort of workaround (I thought about writing a method but so far every attempt to do that has resulted in the compiler screaming at me).

On a side note is it possible to have a description for just the first time something is seen and then a usuall description afterwards or is my best bet to just add <> at the end of the description?

Any help would be much appreciated

Kel

Without testing this, it appears to me that you’re defining the clothingType property on individual objects ofKind Clothes. It appears you’re trying to use it as if it were a class property. I’m not sure how practical that would be.

Let’s suppose you have classes Hat and Shoes. You want to prevent the player from donning a hat if he’s already wearing a hat. The way I would do this would be to add a check() (not a verify()) to dobjFor(Wear) in the Hat class, which iterates through all of the objects in the game that are ofKind(Hat), to see if any of them are wornBy == me. If so, the check() rule would call failCheck ('You’re already wearing a hat. ');

Does that help?

Alternatively, you can store a table of worn clothing in the Actor, and update that whenever a Clothes object is worn or removed.

modify Actor
	wornClothes = static new LookupTable()
;

class Clothes: Wearable
	clothingType = nil
	
	dobjFor(Doff) {
		action() {
			gActor.wornClothes[clothingType] = nil;
			inherited;
		}
	}

	dobjFor(Wear) {
		check() {
			if (gActor.wornClothes[clothingType] != nil)
				failCheck('You would need to take off
					<<gActor.wornClothes[clothingType].theName>> first. ');
		}
		action() {
			gActor.wornClothes[clothingType] = self;
			inherited;
		}
	}
;

// types of clothing
enum shirt, pants, jacket;

// convenience classes for each clothing type
class Shirt: Clothes
	clothingType = shirt
;

bedroom: Room 'bedroom' 'bedroom'
	"Just another bedroom. "
;

+ shortShirt: Shirt 'short sleeved shirt' 'short-sleeved shirt'
	"A short-sleeved shirt. "
;

+ longShirt: Shirt 'long sleeved shirt' 'long-sleeved shirt'
	"A long-sleeved shirt. "
;

The reason it didn’t work your way is that each individual clothing item starts off with clothingType set to nil. So your code doesn’t block any article from being worn. To work around this you either need to iterate through all clothing objects, testing whether another article of that type is being worn, or store that data in some central place so that it’s easy to check.

It kinda helps, I was using check instead of verify til I noticed in one of the reference charts though for what I’m doing it seems to have worked the same.

I didn’t realise that you could check through all objects of a certain class hence making a property, that would be much simpler, is there also a way of finding out all the items a character is wearing? I’d like to be able to see what they look like in the mirror too

I like that idea too bcressey, I had seperate subclasses of clothes too so I could just set the clothingType property to that particular type rather than having the whole thing written out for each one, I figured that it would just act on whichever value is in that property but it doesn’t seem to be the case

You can use initSpecialDesc to change the way an object is initially described in the room listing.

bedroom: Room 'bedroom' 'bedroom'
	"Just another bedroom. "
;

+ longShirt: Shirt 'long sleeved shirt' 'long-sleeved shirt'
	"A long-sleeved shirt. "
;

+ shortShirt: Shirt 'short sleeved favorite shirt' 'short-sleeved shirt'
	"A short-sleeved shirt. "
	initSpecialDesc = "And look, there's your favorite shirt! <<glimpse>>"
	isInInitState = (!glimpsed)
	specialDescBeforeContents = nil
	glimpsed = nil
	glimpse() { glimpsed = true; return ''; }
;

Produces this output:

class Shirt: Clothes clothingType = shirt

Actually that’s exactly how I did it… Little question, is it necessary to put ‘class’ at the beggining when defining a subclass? that could also be part of the problem :confused:

EDIT > Actually I see what you did there, adding the clothingType to the list in Actor, like I say I had it pointing to a property. Your solution kills two birds and gets me another step closer to what I’m after

Thank youuuuuuuuuuu!!
I had a feeling about initSpecialDesc when I first started playing with TADS but at the time most of it was going over my head, I’ll definately have to have a play with things now

The class keyword is not necessary but I use it for the sake of readability.

Here’s a basic mirror implementation.

bedroom: Room 'bedroom' 'bedroom'
	"Just another bedroom. "
;

+ longShirt: Shirt 'long sleeved shirt' 'long-sleeved shirt'
	"A long-sleeved shirt. "
;

+ shortShirt: Shirt 'short sleeved favorite shirt' 'short-sleeved shirt'
	"A short-sleeved shirt. "
;

+ mirror: Fixture 'mirror' 'mirror'
	initSpecialDesc = "There is a mirror on the wall. "
	dobjFor(Examine) {
		action() {
			local clothes = new Vector(); 
			gActor.wornClothes.forEach(new function(item) {
				if (item != nil)
					clothes.append(item);
			});
			clothes = clothes.toList;
			nestedAction(Examine, gActor);
			if (clothes.length) {
				local lister = new WearingLister;
				"You are wearing <<lister.showListAll(clothes, 0, 0)>>. ";
			}
		}
	}
;

Oh thanks!

I was looking forward to using lists and vectors, I’ll be honest I haven’t tried touching them for the moment until I could get used to using some of the basics (knowing what TADS is actually doing with values and such) I’ve been playing with it around a week whilst working through the tutorials and guides so I’m not overly confident when it comes to making lists and manipulating them just yet but I can see how it works no problem.

Do vectors work the same way as lists in the way that in the example before it references the object to the list rather than a string value? I noticed that you used [clothingType].theName earlier and figured it was a reference to the name of the object as apposed to the name of the string at that index. What I’m asking is could I use that vector to use another property of objects within it?

The gActor.wornClothes property points to a lookup table, which is similar to a vector (array) except the index doesn’t have to be a number. In this case it’s an enum value - enums are a bit like strings that don’t need quotes around them, and a bit like a numbered sequence without the number.

Enums are useful because the compiler will throw an error if you use a value that isn’t defined. For example:

enum shirt, pants, jacket;

longShirt: Clothes 'long sleeved shirt' 'long-sleeved shirt'
	"A long-sleeved shirt. "
	clothingType = Shirt
;

shortShirt: Clothes 'short sleeved favorite shirt' 'short-sleeved shirt'
	"A short-sleeved shirt. "
	clothingType = shirt
;

The compiler will tell you that Shirt is not defined. Whereas if you use string values instead of enum values:

longShirt: Clothes 'long sleeved shirt' 'long-sleeved shirt'
	"A long-sleeved shirt. "
	clothingType = 'Shirt'
;

shortShirt: Clothes 'short sleeved favorite shirt' 'short-sleeved shirt'
	"A short-sleeved shirt. "
	clothingType = 'shirt'
;

The compiler will allow it, since both ‘shirt’ and ‘Shirt’ are legal values for a lookup table’s index. Since TADS is case-sensitive, the player can end up wearing both shirts at once.

In the code for the wearing action, the clothing object stores a reference to itself in the table, indexed by its type. The square brackets are always an indexing operation; the container name is the part before the left bracket, and anything after the right bracket applies to the value.

So you can find the shirt that an actor is wearing by checking gActor.wornClothes[shirt]; that will either return nil (if no shirt is worn) or the shirt object being worn.

To answer your question, you can use gActor.wornClothes[shirt].theName, or gActor.wornClothes[shirt].aName, or any other property. The only thing to watch out for is that you don’t ask for a property unless you’ve verified that the stored value isn’t nil. Otherwise you get a runtime error (because nil isn’t an object and doesn’t have properties).

You could also store the property you want in the table instead; for example, gActor.wornClothes[shirt] = shirt.theName. This stores a string rather than an object reference. It’s less useful as a general approach because you can’t easily get back from that string to the object it came from.

That’s pretty neat, and a huge help too! Thanks for the explanation I didn’t realise you were storing enums there. I kinda missed them earlier though it seems to compile fine with the clothingType defined the same as the class (since the class is already defined I’m guessing).

Storing a string does seem a little obsolete if you can store anything in it’s place, I’m sure I’ll think of a use for something along those lines at some point though. I do like the idea of being use a look up table to look at the same property of a bunch of different objects, I have a strong feeling I’m setting myself up for alot of headaches when I get around to using them together such as making the mirror describe how the clothes look on me rather than what I’m actually wearing (I’m aiming to produce something that reads like a story as you play through it) but I’ll worry about that later.

Thank you so so much again

Kelly J