Referring to Objects in Methods, and Creating Lists?

Hey everyone,

I’m fairly new to TADS 3, but not to IF authoring in general. I’ve worked with Inform 7 in the past, and, having an affinity for programming, I thought I would give TADS a go. I love it, and I’m currently trying to develop a skill system with dynamic/randomly determined outcomes for certain actions. I’ve got a lot of it working, but in an attempt to consolidate my code, I kind of entered some unfamiliar territory that I was hoping I could find some help with.

Here’s where I started to get tripped up. I created a new class of object, called Skill, which has a number of properties, which are all supposed to refer to names of other properties found in other objects (things, actors, and wearables, specifically). Here’s what the class declaration looks like, as well as a specific skill object I created.

[code]class Skill : object
sName = nil
cBName = nil
wBName = nil
cBonusName = nil
wBonusName = nil
;

balance : Skill
sName = sBalance
cBName = cBBalance
wBName = wBBalance
cBonusName = cBonusBalance
wBonusName = wBonusBalance
;[/code]

Now, here’s the meat of my questions. Having written and tested some other code, I’ve hit a series of roadblocks.
#1. I can’t find a way to get new functions to refer to specific skill objects, such as balance. For example, the following code does basically nothing, I think because the program isn’t actually looking for an object called “balance” even if the method is called with the argument as “balance.”

totalSkill(skill) { local s = skill.sName; "<<skill.sName>> \n"; "<<s>>\n"; local c = skill.cBName; "<<skill.cBName>> \n"; "<<c>>\n"; local w = skill.wBName; "<<skill.wBName>> \n"; "<<w>>\n"; //return(&s + &c + &w); }
(Aside: Most of the double-quoted strings in this code were put there for debugging purposes, and went completely ‘undsaid’ when the method was executed)

#2. I’m a bit confused as to when to use ‘&’ and when not to use it…by my best guess and understanding, I’m using these symbols correctly here, but I’m also willing to acknowledge that I’m wrong.

#3. Finally, is there any way to get the program to dynamically create a list of objects of a certain class, like my skill class, so that I could execute code using a foreach loop?

Thanks for any help you can offer. I realize that creating something like this might be a bit much for a newcomer, but it’s an idea I’ve had for years, since I first started working with IF.

It’s not clear to me what values or data have been assigned to sBalance, cBBalance, and so on in the balance object, as you haven’t given us that portion of the code. When I assign single-quoted strings to the sName and cBName properties of the balance object, I get the desired printout. (BTW, you can use “\n”, but normal T3 is “<.p>”, which puts a standard white-space gap at the end of the paragraph.)

I’ve seldom used the & operator. Checking p. 40 of Learning T3, it appears this is used mainly when you need to change the value of a double-quoted string without causing it to print. Unlike C and C++, T3 seldom makes use of arguments passed by reference.

look up the ofKind keyword: I’m pretty sure it can be used to build a list of objects of a certain class at run-time. My coding is pretty rusty, so I’m not going to try to whip that up, at least not tonight.

To elaborate slightly, this works for me. I don’t know if it does what you have in mind. As is customary in my testing code, the way to test the method is ‘eat banana’.

[code]startRoom: Room ‘Start Room’
"This is the starting room. "
;

  • me: Actor
    ;

++ banana: Food ‘banana’ ‘banana’
"It looks yummy. "
dobjFor(Eat) {
action() {
totalSkill(balance);
}
}
totalSkill(skill)
{
local s = skill.sName;
“<> <.p>”;
local c = skill.cBName;
“<> <.p>”;
local w = skill.wBName;
“<> <.p>”;
local cBonus = skill.cBonusVal;
"<> ";
}
;

class Skill : object
sName = nil
cBName = nil
wBName = nil
cBonusName = nil
wBonusName = nil
;

balance : Skill
sName = ‘sBalance’
cBName = ‘cBBalance’
wBName = ‘wBBalance’
cBonusVal = 5
;
[/code]

I’m not sure whatever you need property pointers in your case, maybe Jim is right that you don’t need them at all. But to extend a little on the topic of & operator, simple use looks like this:

anObject: object prop = 'test' propPointer = &prop ;

And then somewhere else you wold use: "<<anObject.(anObject.propPointer)>> ";

You may look on PresentLater class in the adv3 library which does precisely this with its makePresentByKey method:

    /* 
     *   Bring every PresentLater object with the given key into the game.
     *   Note that this is a "class" method that you call on PresentLater
     *   itself:
     *   
     *   PresentLater.makePresentByKey('foo'); 
     */
    makePresentByKey(key)
    {
        /* 
         *   scan every PresentLater object, and move each one with the
         *   given key into the game 
         */
        forEachInstance(PresentLater, function(obj) {
            if (obj.plKey == key)
                obj.makePresent();
        });
    }

Thanks for the help, but I think I need to clarify a bit more what the name properties and such are supposed to be. I’ve applied the following code to actors:

modify Actor //Initialize actors' base skills to 0 sBalance = 0 sClimb = 0 sJump = 0 sSwim = 0 //Initialize actors' carry bonuses to 0 cBBalance = 0 cBClimb = 0 cBJump = 0 cBSwim = 0 //Initialize actors' worn bonuses to 0 wBBalance = 0 wBClimb = 0 wBJump = 0 wBSwim = 0

Hopefully this helps explain what the totalSkill method is supposed to do. In theory, when the totalSkill(skill) method is called, the method will refer to the specific skill given as the argument, and find out the names of the specific properties we’re looking for (for example, sBalance, cBBalance, and wBBalance), and add them up for that specific actor, returning them at the end of the method.

For reference, cBalance refers to items being carried by that actor, that provide a bonus to balance (the bonus to the actor is applied when the object is moved into the actor, and removed when the object is removed from the actor). wBalance is similar, but applies when makeWorn() is called. I have this latter stuff all working, FYI.

My problem is still getting the totalSkill(skill) method to work. I need it to refer to the specific properties that the actor has–for each relevant property it needs to get the name of the properties, find the property in the actor, get the number stored in the property, and then return the total of the three numbers.

I hope this makes clearer what I’m trying to accomplish.

Ah – I see where you’re going with this. To be honest, I’m kind of a ham-fisted amateur programmer. I often do things the primitive way. In this case, I’d just write out the code iteratively and not bother with property pointers. A software engineer would of course want to do a prettier version … but the thing is, IF is quite often a mass of specific cases, not a type of programming where general-purpose solutions are necessarily the best. (For instance, if you have 50 Actors and 43 skills, nobody is likely to want to play your game!)

So here’s how I would do it – the easy, primitive way. As before, type ‘eat banana’ to run the method.

[code]class Skill: object;

balance: Skill;
climb: Skill;
jump: Skill;
swim: Skill;

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

modify Actor
//Initialize actors’ base skills to 0
sBalance = 0
sClimb = 0
sJump = 0
sSwim = 0
//Initialize actors’ carry bonuses to 0
cBBalance = 0
cBClimb = 0
cBJump = 0
cBSwim = 0
//Initialize actors’ worn bonuses to 0
wBBalance = 0
wBClimb = 0
wBJump = 0
wBSwim = 0

totalSkill(skill)
{
    local s;
    local cB;
    local wB;
    
    if (skill == balance) {
        s = sBalance;
        cB = cBBalance;
        wB = wBBalance;
    }
    else if (skill == climb) {
        s = sClimb;
        cB = cBClimb;
        wB = wBClimb;
    }
    else if (skill == jump) {
        s = sJump;
        cB = cBJump;
        wB = wBJump;
    }
    else if (skill == swim) {
        s = sSwim;
        cB = cBSwim;
        wB = wBSwim;
    }
    else "Error -- this should never print. ";
    return s + cB + wB;
}

;

  • me: Actor
    sBalance = 1
    sClimb = 2
    sJump = 3
    sSwim = 4

    cBBalance = 2
    cBClimb = 4
    cBJump = 6
    cBSwim = 8

    wBBalance = 3
    wBClimb = 6
    wBJump = 9
    wBSwim = 12
    ;

++ banana: Food ‘banana’ ‘banana’
"It looks yummy. "
dobjFor(Eat) {
action() {
local sk = me.totalSkill(balance);
“Your balance is <>.\n”;
sk = me.totalSkill(climb);
“Your climbing skill is <>.\n”;
sk = me.totalSkill(jump);
“Your jumping skill is <>.\n”;
sk = me.totalSkill(swim);
“Your swimming skill is <>.\n”;
}
}
;[/code]

Ironically, that style of coding is almost exactly what I used to have. Then when I realized I wanted to put in more skills, and make the rules more easily modifiable (in the event that I chose to publish them for anyone to use), I thought of consolidating everything into a few easy to access methods and functions–but it turns out that those methods and functions are infuriatingly difficult to code. I might go back to the old way, but I’d still prefer to be able to make this neater, consolidated version fly.

Here’s another idea. Try replacing the relevant blocks in the code above with this. The assumption that I’m making is that gActor will always point to the Actor whose skills you want to get at. If this is not the case, then you could modify it – for instance, by sending the desired Actor object as a parameter to the s1(), cB2(), and wB3() methods.

[code]class Skill: object;

balance: Skill
s1 () { return gActor.sBalance; }
cB2 () { return gActor.cBBalance; }
wB3 () { return gActor.wBBalance; }
;
climb: Skill
s1 () { return gActor.sClimb; }
cB2 () { return gActor.cBClimb; }
wB3 () { return gActor.wBClimb; }
;
;
jump: Skill
s1 () { return gActor.sJump; }
cB2 () { return gActor.cBJump; }
wB3 () { return gActor.wBJump; }
;
;
swim: Skill
s1 () { return gActor.sSwim; }
cB2 () { return gActor.cBSwim; }
wB3 () { return gActor.wBSwim; }
;

////////////////

modify Actor
//Initialize actors’ base skills to 0
sBalance = 0
sClimb = 0
sJump = 0
sSwim = 0
//Initialize actors’ carry bonuses to 0
cBBalance = 0
cBClimb = 0
cBJump = 0
cBSwim = 0
//Initialize actors’ worn bonuses to 0
wBBalance = 0
wBClimb = 0
wBJump = 0
wBSwim = 0

totalSkill(skill)
{
    local s = skill.s1;
    local cB = skill.cB2;
    local wB = skill.wB3;
    
    return s + cB + wB;
}

;[/code]

I really like that last suggestion. I’ll try it out and let you know how it works out.

So that implementation worked like a charm. The only thing left to tackle is my problem with the loop/list. So far, I’ve got this:

skillList : object elements = [balance, climb, jump, swim] ;

And then later, I call on it using this:

foreach(local idx in skillList.elements)

What I’m hoping to do (and I can’t figure out how) is to remove the need for the skillList object, and just dynamically create the list of objects with the class “Skill”, so that if I ever want to add/remove skills, I don’t need to also modify the skillList object.

I’m pretty sure you can do this using the forEachInstance library function to build a list. See p. 156 in Learning T3 for an example. The example only counts the Decoration objects, but you should be able to modify that code so as to add items of a certain class to a list.

Or, adapting the code on that page, something this might be easier to read and debug, as it doesn’t rely on anonymous functions. (Untested.)

local skillList = []; local obj = firstObj(Skill); while(obj != nil) { skillList = skillList.append(obj); obj = nextObj(obj, Skill); }

Okay, so here’s my implementation of the code above. Unfortunately, it now throws an error and a warning.

addCarryBonus(actor, obj) { local skillList = []; local skl = firstObj(Skill); while(skl != nil) { skillList = skillList.append(skl); skl = nextObj(obj, Skill); } foreach(local idx in skillList); { if(idx.cBonusNum(obj) != 0) { idx.addCBonus(actor, self); } } }

Apparently, the “idx” variable in my foreach loop is being read as something different–the system isn’t recognizing it when it tries to call on it during the loop.

You have a semicolon at the end of your foreach line. Try deleting that – it’s probably the source of the errors.

Duh. I can’t believe I didn’t see that. Thanks. I also spotted another error in there, in case anyone else is reading this thread and trying to use the same code. The line that currently looks like this:

skl = nextObj(obj, Skill); should say “skl” within the parentheses, instead of “obj.”