Random Questions

Hello,

I figured I’d make another game help thread like I did earlier in the year. I’m getting questions, but don’t want multiple threads.
Right now I’m having a problem with output.

You see a satchel (which contains some cooking scrolls) here. On the spice rack are a jar (which contains some thyme spice), a jar (which contains some pikun spice), and a jar (which contains some safrin spice).

does that look weird? are a jar, and then other jars.

I also have to get over disambiguation, but it’s kind of confusing to me. Say I want to have the player be able to buy jars of stuff. They could have 10 jars in their inventory. It doesn’t make sense to come up with disambiguation names, or names, or different vocab words for every instance. I could change vovabLikelihood on every instance of same named objects to one above each other, but that doesn’t sound right either…

Is there a clear cut solution to this?

Thanks,
Jeremy.

Based on what you’ve said, you want to set isEquivalent = true for the jars in your game. When you’ve done that, the 10 jars in your inventory should behave the way you’re saying you want. For the jars on the shelf with different contents, the parser will go to fairly great lengths to differentiate them by their contents, their location, etc. It’s been a while since I did this, but try it and see.

Edit

I just fixed something. I noticed one of the spices had the same vocabWord set. It made it easier to grab the spices by name from the jars, but it didn’t fix all the problems. With three jars stacked you can “put spice in jar.” but after that it doesn’t know how to put another kind of spice in a different jar.

Original

It doesn’t work right. The jars show up on the spice rack as The spice rack has 3 jars. The problem is I can say take jar three times to take all three, but the contents are wrecked. I only get one content. When I type look at jar, I only get the description of jar, and I can’t type look at safrin spice, even though I’ve got it in one of the jars…

Two fantasy spices, one real world spice, and three jars.

thymeJar : Container, Dumpable
    name = 'jar'
    vocabWords = 'thyme jar'
    disambigName = 'jar'
    desc = "A tiny glass jar, used for holding spices, or other small contents. "
    location = greysSpiceRack
    bulkCapacity = 1
    bulk = 1
    isEquivalent = true
;

pikunJar : Container, Dumpable
    name = 'jar'
    vocabWords = ' jar'
    disambigName = 'jar'
    desc = "A tiny glass jar, used for holding spices, or other small contents. "
    location = greysSpiceRack
    bulkCapacity = 1
    bulk = 1
    isEquivalent = true
;

safrinJar : Container, Dumpable
    name = 'jar'
    vocabWords = 'jar'
    disambigName = 'jar'
    desc = "A tiny glass jar, used for holding spices, or other small contents. "
    location = greysSpiceRack
    bulkCapacity = 1
    bulk = 1
    isEquivalent = true
;


thyme : Food
    name = 'thyme spice'
    vocabWords = 'thyme spice'
    uses = 30
    type = 'spice'
    location = thymeJar
    becomesPluralDropped = true
    beceomesSingularTaken = true
    isPlural = true
    desc = "These dried thyme leaves resemble hundreds of
        tiny green and light brown sticks."
    bulk = 1
    isEquivalent = true
    nutrition = 1
    canRot = true
    shelfLife = 300
  
;

pikun : Food
    name = 'pikun spice'
    vocabWords = 'pikun spice'
    uses = 20
    type = 'spice'
    location = pikunJar
    becomesPluralDropped = true
    beceomesSingularTaken = true
    isPlural = true
    desc = "Pikun spice is a reddish-orange spice, made from the ground seeds of a 
            pikun tree."
    isEquivalent = true
    bulk = 1
    nutrition = 1
    canRot = true
    shelfLife = 300
  
;

safrin : Food
    name = 'safrin spice'
    vocabWords = 'safrin spice'
    uses = 20
    type = 'spice'
    location = safrinJar
    becomesPluralDropped = true
    beceomesSingularTaken = true
    isPlural = true
    desc = "Safrin smells sweet. It's made from silver buds, from a small herb plant with silver leaves."
    isEquivalent = true
    bulk = 1
    nutrition = 1
    canRot = true
    shelfLife = 300
  
;

Meh, the jars don’t seem worth the trouble. I just removed them, and put the spice on the rack. It’s just easier to have single items, that can be set it isEquivalent. I’d still like to hear advice on it, if there is any.

Something else is wrong here though. Check the output:

You see a satchel (which contains some cooking scrolls) and some frying pans here. On the spice rack are some thyme spice, some pikun spice, and some safrin spice.

Is there any way I can change ‘are’ to ‘is?’

It should read: on the spice rack is some thyme spice, some pikun spice, and some safrin spice.

I think either would be grammatically wrong?

To bad it wouldn’t say: on the spice rack there is some…

Not sure I agree with you about what is and isn’t grammatical. But regardless, try this:

// Approach suggested by Jeremy
spiceRack: Surface
[…]
contentsLister: surfaceContentsLister {
showListPrefixWide(itemCount, pov, parent)
{ "On <<parent.theNameObj>> there is "; }
showListPrefixTall(itemCount, pov, parent)
{ “On <<parent.theNameObj>> there is:”; }
}
;

// Traditional approach
spiceRack: Surface
[…]
contentsLister: surfaceContentsLister {
showListPrefixWide(itemCount, pov, parent)
{ "On <<parent.theNameObj>> there <<itemCount == 1 ? ‘is’ : ‘are’>> "; }
showListPrefixTall(itemCount, pov, parent)
{ “On <<parent.theNameObj>> there <<itemCount == 1 ? ‘is’ : ‘are’>>:”; }
}
;

Also, if you got rid of your jars, I would get rid of the isEquivalent on your other objects. It serves no purpose to just put isEquivalent = true on everything. The purpose of isEquivalent is to make it so, e.g. for the code below, it will say “You are carrying 4 pencils” rather than “You are carrying a pencil, a pencil, a pencil, and a pencil.”

me: Actor;
+Pencil;
+Pencil;
+Pencil;
+Pencil;

Thanks for the tips. I’m having an issue wrapping my head around a Drinkable, or Liquid class. I know this is one of the toughest ones. It’s just, there can’t be an RPG without ale, and brooks! The problem I’m having with liquid is that I don’t want the user to be able to take it. Tads3 expects a take before drink. I can make the item be dropped again after drinking, but that doesn’t cover take. If I make take impossible, drink fails. Has a work around been done for this?

One thing that bothers me about Tads is all the pre-made assumptions. It’s like tads came a little too high level. All commands, and outputting should have been left up to the authors. Building high level, and then expecting us to go low level to work around is a bit of a backwards plan, lol. Only works for really smart people.

This is Drinkable, though I may change it to Liquid.

class Drinkable : Thing
    isFinite = nil
    canDrink = true
    isNoxious = nil
    canTake = nil
    canFill = nil
    thirstFill = 100
    uses = 0
    
    verify() {  logical; }
    dobjFor(Drink)
    {
        verify(){logical;}
        action()
        {
            if(canDrink)
            {
                
                if(isFinite)
                {
                   
                     // drink 
                        uses -= 1;
                        if(gActor == me)
                        {
                            
                            gActor.thirstBar += thirstFill;
                            
                            if(gActor.thirstBar > gActor.maxThirst)
                            {
                                
                                gActor.thirstBar = gActor.maxThirst;
                                
                            }
                            
                            "<<gActor.name>> takes a refreshing drink of {the dobj/him}.";
                            
                        }else{
                            
                            "{You/he} drinks {the dobj/him}.";
                        }
                        
                    
                   
                    // end if finite
                }else{
                    
                    if(gActor == me)
                        {
                            
                            gActor.thirstBar += thirstFill;
                            
                            if(gActor.thirstBar > gActor.maxThirst)
                            {
                                
                                gActor.thirstBar = gActor.maxThirst;
                                
                            }
                            
                            "<<gActor.name>> tanks a refreshing drink of {the dobj/him}.";
                            
                        }else{
                            
                            "{You/he} drinks {the dobj/him}.";
                        }
                    
                    // end if not finite
                }
                
                if(isFinite && uses <= 0)
                {
                    
                    // remove finite source, all drinks used
                    
                    gDobj.moveInto(nil);
                    
                }
              // end if canDrink  
            }else{
                // can't drink
                if(isNoxious)
                {
                    
                    "{The dobj/he} is far too noxious.";
                       
                }else{
                    
                    "<<gActor.theName>> has second thoughts about drinking {the dobj/he}.";
                    
                }
            }
            
            gDobj.moveInto(gActor.getOutermostRoom());
            // end action
        }
        // drink
    }
    
  
;

Woops. I solved the liquid issue by removing a preCond in things.t

Here’s one that gets me. I’m trying to take an object, but I get an un-usable object in my inventory. It shows up as listed, but I can’t drop it, look it, or nothing. The culprit is a frying pan.

greysPans : Thing
    name = 'frying pans'
    vocabWords = 'castiron iron frying pan*pans'
    location = greysKitchen
    isPlural = true
    oneTaken = nil
    desc = "Cast iron frying pans, with wooden handles. They're in pretty good shape, but
         some bare the marks of daily use. A couple are filled with scratches, and dings,
        but they're hardly noticable without close inspection."
    disambigName = 'hanging pans'
    
    dobjFor(Take)
    {
        
        action()
        {
                if(!oneTaken)
                {
                    local hasPan = nil;
                        
                    foreach(obj in gActor.contents)
                    {
                        
                        if(obj.name == 'frying pan')
                        {
                            hasPan = true;
                        }
                    }
                    
                    if(!hasPan)
                    {
                        //BUG : Taking the pan puts an un-usable fying pan in inventory.
                        fryingPan = new Pan;
                        fryingPan.name = 'frying pan';
                        fryingPan.vocabWords = 'prized frying pan';
                        fryingPan.disambigName = 'prized pan';
                        fryingPan.desc = '<<me.name>> knows this as Greys prized frying pan. It\'s formed 
                                          of solid cast iron, and has a dark stained handle. Grey often remarked
                                            in its ability to cook perfect meals, without needing much attention.';
                        fryingPan.location = greysKitchen;
                        fryingPan.bulkCapacity = 10;
                        fryingPan.weightCapacity = 30;
                        fryingPan.bulk = 8;
                        fryingPan.weight = 8;
                    
                        if(fryingPan.bulk + gActor.getBulkHeld() < gActor.bulkCapacity &&
                           fryingPan.weight + gActor.getWeightHeld() < gActor.weightCapacity)
                        {
                            
                            fryingPan.moveInto(gActor);
                           
                            if(gActor == me)
                            {
                                
                                 "<<me.name>> takes one of Grey's prized frying pans.";
                                
                            }else{
                                
                                "{You/he} takes one of Grey's prized frying pans.";
                                
                                
                            }
                        
                        }else{
                        
                            fryingPan.moveInto(nil);
                        
                            if(gActor == me)
                            {
                                
                                "<<me.name>> has too many possessions.";
                                
                            }else{
                                
                                "{You/he} has too many possessions.";
                                
                            }
                            
                        
                        }
                     
                    
                    }else{
                        
                        if(gActor == me)
                        {
                            
                            "<<me.name>> already has a frying pan.";
                            
                        }else{
                            
                            "{You/he} already has a frying pan.";
                            
                        }
                        
                    }
                    
                }else{
                    
                    "You've already taken on of Grey's frying pans.";
                    
                }
            // end action
            }        
          // end dobjFor(Take)
        }
;

There is documentation for this stuff. In my experience you don’t get to be proficient at TADS 3 until you’ve read a fair proportion of what’s out there to read.

In this case, the TADS 3 Technical Manual mentions a nice example similar to this. See “Pre-Conditions” on:
tads.org/t3doc/doc/techman/t3res.htm

For what you’re doing, I would consider the following:

class Drinkable : Thing
[…]
dobjFor(Drink)
{
[…]
preCond = (inherited() - [objHeld])
}
;

My current issue is the frying pan posted just under. I solved the liquid issue via preCond. I just took the main preCond out of the action itself, under thing.t, as it’s useless.

In the frying pan issue, I’m instantiating an object that goes into my inventory, but can’t be used or accessed. I’m not sure what I’m missing.

I read the docs though. I’ve been reading them for weeks, and cross checking every page. I can only soak in so much, or you know, come across an issue that I just can’t find an answer to…

The main thing is I can’t see your definition of the Pan class. If you want help, please include that definition. The most obvious possibility that comes to mind is that maybe your Pan class doesn’t inherit from Thing, in which case that would explain it.

Thanks :slight_smile: Here’s the full bit. It inherits Container, which should already inherit Thing. I can type “get pan” just fine, and it says I take one. I can type ‘i’ and get “you are carrying a frying pan.” but when I type look at frying pan, or drop pan. The output is “I don’t see any frying pan here.”

pan class

class Pan : Container name = '' weight = 0 bulk = 0 bulkCapacity = 0 weightCapacity = 30 ;

code

greysPans : Thing
    name = 'frying pans'
    vocabWords = 'castiron iron frying pan*pans'
    location = greysKitchen
    isPlural = true
    oneTaken = nil
    desc = "Cast iron frying pans, with wooden handles. They're in pretty good shape, but
         some bare the marks of daily use. A couple are filled with scratches, and dings,
        but they're hardly noticable without close inspection."
    disambigName = 'hanging pans'
    
    dobjFor(Take)
    {
        
        action()
        {
                if(!oneTaken)
                {
                    local hasPan = nil;
                        
                    foreach(obj in gActor.contents)
                    {
                        
                        if(obj.name == 'frying pan')
                        {
                            hasPan = true;
                        }
                    }
                    
                    if(!hasPan)
                    {
                        //BUG : Taking the pan puts an un-usable fying pan in inventory.
                        fryingPan = new Pan;
                        fryingPan.name = 'frying pan';
                        fryingPan.vocabWords = 'prized frying pan';
                        fryingPan.disambigName = 'prized pan';
                        fryingPan.desc = '<<me.name>> knows this as Greys prized frying pan. It\'s formed 
                                          of solid cast iron, and has a dark stained handle. Grey often remarked
                                            in its ability to cook perfect meals, without needing much attention.';
                        fryingPan.location = greysKitchen;
                        fryingPan.bulkCapacity = 10;
                        fryingPan.weightCapacity = 30;
                        fryingPan.bulk = 8;
                        fryingPan.weight = 8;
                    
                        if(fryingPan.bulk + gActor.getBulkHeld() < gActor.bulkCapacity &&
                           fryingPan.weight + gActor.getWeightHeld() < gActor.weightCapacity)
                        {
                            
                            fryingPan.moveInto(gActor);
                           
                            if(gActor == me)
                            {
                                
                                 "<<me.name>> takes one of Grey's prized frying pans.";
                                
                            }else{
                                
                                "{You/he} takes one of Grey's prized frying pans.";
                                
                                
                            }
                        
                        }else{
                        
                            fryingPan.moveInto(nil);
                        
                            if(gActor == me)
                            {
                                
                                "<<me.name>> has too many possessions.";
                                
                            }else{
                                
                                "{You/he} has too many possessions.";
                                
                            }
                            
                        
                        }
                     
                    
                    }else{
                        
                        if(gActor == me)
                        {
                            
                            "<<me.name>> already has a frying pan.";
                            
                        }else{
                            
                            "{You/he} already has a frying pan.";
                            
                        }
                        
                    }
                    
                }else{
                    
                    "You've already taken on of Grey's frying pans.";
                    
                }
            // end action
            }        
          // end dobjFor(Take)
        }
;

You are carrying a frying pan, and you’re wearing some shaggy grass shorts.

Looking at the posted code the only problem I see is that vocabWords has no special meaning after the object is created, which the fryingPan = new Pan line of code did. If you put the vocabWords on your Pan class object all pan objects created will inherit that vocabulary.

To fix your code as it is right now change the fryingPan.vocabWords = ‘prized frying pan’; to fryingPan.initializeVocabWith(‘prized frying pan’);

That will initialize the vocabulary of the new pan and your pan should work.

On more general level you seem to fight a lot with TADS world model. There should be no need to create objects dynamically (fryingPan = new Pan), this is needed for really special cases and not common in normal games. For example in Return to Ditch Day, which is Mike Robert’s example game with published source (you should read the source to learn many tricks), there is only one game object constructed dynamically (store receipt) because the player can get unlimited number of receipts depending on how many time he buys something. When the player is allowed to pick only one frying pan (or few) as it seems from your code, it is more practical to prepare the object statically upfront and then use some mechanism to hide it until necessary. Read about Hidden and PresentLater classes.

TADS has very elaborate and very polished world model and you should learn how to use the world model properly before trying to go around and change its behaviour. You need to understand how all fits together else you will struggle a lot when you accidentally break something. For example I’ve noticed you are testing container capacity: if(fryingPan.bulk + gActor.getBulkHeld() < gActor.bulkCapacity)… TADS world model already checks container capacity for you and you should not try to duplicate this. If you need specific error messages instead of default ones then you should supply only messages. Or maybe the problem is that you accidentally broke standard behaviour because it seems to me you don’t know about importance of inherited keyword. Please read about it in Learning TADS3, its discussed first time in chapter 5.1.8 and following coding excursus in chapter 5.2.

Which brings me to another hint. When you override any method you should look into TADS3 Library Reference Manual, search for the class and specific method and look to description and look how the method is programmed, what it does by default. Many times you want the behaviour and you should call inherited() and only supply some text. You seem to forget about inherited in action() block.

Assuming that something in library is useless and then removing it in library code itself is very dangerous thing. You should never ever do this, becase then nobody will ever be able to help you when you damage your library code. If some modification is necessary TADS has modify and replace keyword to override any library code transparently.

On object templates, but wouldn’t an instantiated object in a dobjFor(Take) be a different case? Would moveInto overlook this?

Doesn’t really make sense in the terms of class to programming. A class is supposed to be a template, hard-coding vocabWords breaks this construct. You might want a ‘sauce pan’ ‘pie pan’ ‘frying pan’ ‘dish pan’ maybe even a ‘pizza pan’.

I’m still getting an unusable pan. I’m trying to get my head around when to use variables in my class construct, and when not to. I understand to add custom variables in the class, but not sure when to add name, and vocabWords. I thought they would be assigned when the object is instantiated.

class Pan : Container
    name = 'pan'
    vocabWords = 'pan'
    weight = 0
    bulk = 0
    bulkCapacity = 0
    weightCapacity = 30  
;
greysPans : Thing
    name = 'frying pans'
    vocabWords = 'castiron iron frying pan*pans'
    location = greysKitchen
    isPlural = true
    oneTaken = nil
    desc = "Cast iron frying pans, with wooden handles. They're in pretty good shape, but
         some bare the marks of daily use. A couple are filled with scratches, and dings,
        but they're hardly noticeable without close inspection."
    disambigName = 'hanging pans'
    
    dobjFor(Take)
    {
        
        action()
        {
                if(!oneTaken)
                {
                    local hasPan = nil;
                        
                    foreach(obj in gActor.contents)
                    {
                        
                        if(obj.name == 'frying pan')
                        {
                            hasPan = true;
                        }
                    }
                    
                    if(!hasPan)
                    {
                        //BUG : Taking the pan puts an un-usable fying pan in inventory.
                        fryingPan = new Pan;
                        fryingPan.name = 'frying pan';
                        fryingPan.initializeVocabWith('prized frying pan');
                        fryingPan.disambigName = 'prized pan';
                        fryingPan.desc = '<<me.name>> knows this as Greys prized frying pan. It\'s formed 
                                          of solid cast iron, and has a dark stained handle. Grey often remarked
                                            in its ability to cook perfect meals, without needing much attention.';
                        fryingPan.location = greysKitchen;
                        fryingPan.bulkCapacity = 10;
                        fryingPan.weightCapacity = 30;
                        fryingPan.bulk = 8;
                        fryingPan.weight = 8;
                    
                        if(fryingPan.bulk + gActor.getBulkHeld() < gActor.bulkCapacity &&
                           fryingPan.weight + gActor.getWeightHeld() < gActor.weightCapacity)
                        {
                            
                            fryingPan.moveInto(gActor);
                           
                            if(gActor == me)
                            {
                                
                                 "<<me.name>> takes one of Grey's prized frying pans.";
                                
                            }else{
                                
                                "{You/he} takes one of Grey's prized frying pans.";
                                
                                
                            }
                        
                        }else{
                        
                            fryingPan.moveInto(nil);
                        
                            if(gActor == me)
                            {
                                
                                "<<me.name>> has too many possessions.";
                                
                            }else{
                                
                                "{You/he} has too many possessions.";
                                
                            }
                            
                        
                        }
                     
                    
                    }else{
                        
                        if(gActor == me)
                        {
                            
                            "<<me.name>> already has a frying pan.";
                            
                        }else{
                            
                            "{You/he} already has a frying pan.";
                            
                        }
                        
                    }
                    
                }else{
                    
                    "You've already taken on of Grey's frying pans.";
                    
                }
            // end action
            }        
          // end dobjFor(Take)
        }
;

This is ridiculous, because I already created a far more advanced armour class, and it instantiates just fine. The only difference is I used a slightly different method of setting the class variables. I made setter functions.

Armour

class Armour: Wearable 
    name = ''
    type = 'armour'
    wearLocation = ''
    craftsmanship = ''
    tn = 0
    ar = 0
    stats = ['combat', 'magic', 'vitality', 'perception', 'strength', 'dexterity', 'wisdom', 'charisma'] 
    statMods = [0, 0, 0, 0, 0, 0, 0, 0]  
    skills = ['fire', 'break', 'track', 'hide', 'spot', 'diplomacy', 'intimidate', 'bluff', 
            'lockpick', 'pickpocket', 'decipher', 'detect traps', 'lore'] 
    skillMods = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 
    ResElems = ['holy', 'fire', 'water', 'lightning', 'earth', 'air', 'dark'] 
    res = [0, 0, 0, 0, 0, 0, 0]
    
     construct(nameSet, vocabWordsSet, wearLocationSet, isPluralSet = nil)
    {
        
        name = nameSet;
        vocabWords = vocabWordsSet;
        wearLocation = wearLocationSet;
        isPlural = isPluralSet;
        
        initializeVocabWith(vocabWords);
        
     }
    
    set_location(locationSet)
    {
    
        location = locationSet;
    }
    
    set_craftsmanship(craftSet)
    {
        
        craftsmanship = craftSet;
        
    }
    
           // set the item description
    set_desc(descSet)
    {
        
        desc = descSet;
        
    }
    
    // set the item owner
    
    set_owner(actor)
    {
        
        owner = actor;
        
    }
    
    // set wornBy
    
    set_wornby(actor)
    {
        
        wornBy = actor;
        
    }
  
    //set tn
    
    set_tn(tnSet)
    {
        
        tn = tnSet;
        
    }
     
    // set Armour Rating
    
    set_ar(arSet)
    {
        
        ar = arSet;
        
    }
    
    puton(surface, obj)
    {
        
        surface.tryMovingObjInto(obj);
        
    }
   
        // set a resistance value for piece of armour 
    set_resistance(element, percent)
    {
        
        switch(element)
        {
            
            case 'holy':
                self.res[1] = percent;
            break;
            case 'fire':
                self.res[2] = percent;
            break;
            case 'water':
                self.res[3] = percent;
            break;
            case 'lightning':
                self.res[4] = percent;
            break;
            case 'earth':
                self.res[5] = percent;
            break;
            case 'air':
                self.res[6] = percent;
            break;
            case 'dark':
                self.res[7] = percent;
            break;

        }
        
    }
   
    // add skill
        // add arrays
    set_skill(skill, mod)
    {
        
        switch(skill)
        {
            
            case 'fire':
                self.skillMods[1] = mod;
            break;
            case 'break':
                self.skillMods[2] = mod;
            break;
            case 'track':       
                self.skillMods[3] = mod;
            break;
            case 'hide':
                self.skillMods[4] = mod;
            break;
            case 'spot':
                self.skillMods[5] = mod;
            break;
            case 'diplomacy':
                self.skillMods[6] = mod;
            break;
            case 'intimidate':  
                self.skillMods[7] = mod;
            break;
            case 'bluff':
                self.skillMods[8] = mod;
            break;
            case 'lockpick':    
                self.skillMods[9] = mod;
            break;
            case 'pickpocket':
                self.skillMods[10] = mod;
            break;
            case 'decipher':
                self.skillMods[11] = mod;
            break;
            case 'detect traps':
                self.skillMods[12] = mod;
            break;
            case 'lore':
                self.skillMods[13] = mod;
            break;
            
        }
        
    }
    
    // add stat bonus 
        // add arrays
    set_stat(stat, mod)
    {
       switch(stat)
        {
            
            case 'combat':
                self.statMods[1] = mod;
            break;
            case 'magic':
                self.statMods[2] = mod;
            break;
            case 'vitality':
                self.statMods[3] = mod;
            break;
            case 'perception':
                self.statMods[4] = mod;
            break;
            case 'strength':
                self.statMods[5] = mod;
            break;
            case 'dexterity':
                self.statMods[6] = mod;
            break;
            case 'wisdom':
                self.statMods[7] = mod;
            break;
            case 'charisma':
                self.statMods[8] = mod;
            break;
            
        }
    }
    
        // handle wearing the item
    dobjFor(Remove)
    {
        
        verify()
        {
          
                if(wornBy != gActor.getIdentityObject())
                    if(gActor == me)
                        illogicalAlready('<<me.name>> is not wearing {the dobj/him}.'); 
                    else 
                        illogicalAlready('<<gActor.aName()>> is not wearing {the dobj/him}.');            
        }
        
        action()
        {
           
        
                
                if(gActor == me)
                {
                    "<<me.name>> removes {the dobj/him}.";
                    
                }else{
                    
                    "<<gActor.aName()>> removes {the dobj/him}.";
                }
             
                me.tn -= gDobj.tn;
                me.ar -= gDobj.ar;
                gDobj.moveInto(gActor);
                gActor.addToContents(gDobj);
                gDobj.wornBy = nil;
                
                          
        }
        
      
    }
        // handle removing the items
    dobjFor(Wear)
    {
        
         verify()
        {
          
                if(wornBy == gActor.getIdentityObject())
                    if(gActor == me)
                        illogicalAlready('<<me.name>> is already wearing {a dobj/him}.'); 
                    else 
                        illogicalAlready('<<gActor.aName()>> is already wearing {a dobj/him}.');  
                
        }
        
        action()
        {
           
             gDobj.wornBy = gActor;
             me.tn += gDobj.tn;
             me.ar += gDobj.ar;
            
              if(gActor == me)
              {
                
                   "<<me.name>> puts on the {the dobj/him}.";
                    
                // end wear by player
              }else{
                
                "<<gActor.aName()>> puts on the {the dobj/him}.";
                
              // end wear by npc or monster
             }
            
            
            
            // End Wear action
        }
        
    }
    
;

shaggyGrassShorts = new Armour('shaggy grass shorts', 'shaggy grass shorts', 'legs', true); shaggyGrassShorts.set_desc('Shaggy strands of grass encircle the waist, and hang to about mid-thigh. \n <em>AR: 1</em>'); shaggyGrassShorts.set_location(startRoom); shaggyGrassShorts.moveInto(me); shaggyGrassShorts.set_wornby(me); shaggyGrassShorts.set_tn(1); shaggyGrassShorts.set_ar(1);

Wait, I got it, but not sure why. I finally got to the point where I could drop the pan. The only issue was I couldn’t look at it, so I had to add dobjFor(Examine)

Not sure why this didn’t inherit from Thing, or Container…

 fryingPan = new Pan('frying pan', 'frying pan', true);
                        fryingPan.set_description('<<me.name>> knows this as Greys prized frying pan. It\'s formed 
                                          of solid cast iron, and has a dark stained handle. Grey often remarked
                                            in its ability to cook perfect meals, without needing much attention.');
                        fryingPan.location = greysKitchen;
                        fryingPan.moveInto(greysKitchen);
                        fryingPan.bulkCapacity = 10;
                        fryingPan.weightCapacity = 30;
                        fryingPan.bulk = 8;
                        fryingPan.weight = 8;
class Pan : Container
    name = ''
    weight = 0
    bulk = 0
    bulkCapacity = 0
    weightCapacity = 30
    desc = ""
   
     construct(nameSet, vocabWordsSet, isPluralSet = nil)
    {
        
        name = nameSet;
        vocabWords = vocabWordsSet;
        isPlural = isPluralSet;
        
        initializeVocabWith(vocabWords);
        
     }
    
    set_description(descSet)
    {
        desc = descSet;
    }
    
    dobjFor(Examine)
    {
        
        action()
        {
            "<<desc>>";
        }
        
    }
    
;

Description (desc property) should be assigned to double quoted string. Double quoted string is not a string in usual sense as seen in other programming languages. Double quoted string is command to print its contents. You can thing of it as a function or when you assign it as function pointer.

Well moveInto is programmers API, that is not checked for container capacity. But players command >take is checked unless you prevent it. You seem try to do some things by yourself which TADS could handle for you and I’m not sure why.

You are right, class is something like a blueprint of a house. Class is in essence a plan how to make an object. In contrast to say C++, TADS class is in fact a specially flagged object which is copied when you make an instance. And because in text adventure game there are lots of objects, TADS allows you to declare instances of objects directly in source code.

When you declare vocabWords in any object in source code, the property is assigned before the object is instantiated, that means before its constructor and constructors of each parent are executed. Thing class on which every physical game object is based inherits from VocabObject class which reads vocabWords in constructor, parses the string to determine what is adjective, noun and so on and initializes parser vocabulary from it. See http://tads.org/t3doc/doc/libref/source/en_us.t.html#332.

When you instantiate object by yourself dynamically using new keyword, you are instantiating in fact default object of some class. Then you add various properties such as name, desc. That’s fine. But for vocabWords it’s too late, they are already processed because constructor already fired. That’s ok, vocabWors is only a shorthand to easily initialize parser, you can modify parser later with calling initializeVocabWith(‘vocab words/string’) or other methods, it just so uncommon to define object dynamically you probably won’t see this in examples. It’s not meant to work this way in 99 % of cases.

Processing vocabWords in constructor is generally an exception, I can’t remember of any other property which would be so sensitive to be defined before instantiation. VocabWords are quite complicated, they are even combined from object and from parent classes (not overriden but combined together) which is quite unusual in sense of OOP programming.

Instead of trying to understand this concept you would benefit more to learn how to properly use Hidden and PresentLater classes (and container hierarchy and plus signs and many more) and make the fryingPan statically declared.

Oh, location property is also processed early and you are meant to use moveInto() or moveIntoForTravel() when you want to change location at run time.

Awesome, thanks for all the great info tomasb. I’ve asked this before, but it’s still giving me troubles. I’m trying to break my game up into various files. Basically I want to store a specific locations objects, rooms, and other such things in a separate .t file.

I open notepad++ and add all my rooms and objecst to .t files. I put the file in my source folder I’ve created. I then right click source forlder add file. I right click the file in the source file and click build settings, I make sure to add the source folder where they’re stored in my project file, but keep getting an error saying that objects can only be defined once. I’m not sure why, as I remove the definitions from the main source file…

It’s a bit weird and main source is getting really complex. Game is going well though. Feeling good about it.

Got a combat system, will be adding magic. Combat already works for the most part :slight_smile:

This should not be problem, in my game I have a separate file for every location and for every major character and few others.

Correct, you should see all your source files under “Source Files” entry in project pane of Workbench.

I don’t know where you are adding the source folder, but nothing like that should be necessary. All the files should be stored in the same directory which is the project directory where main file and makefile and such files are stored.