Affecting action behavior and variables

Hello, I’m new to the board and to TADS3, so sorry for any silly questions.

Anyway, I’ve been trying to make a project and I’ve been stuck on how to have objects affect other objects. In particular, I want to be able to change variables and how objects react on actions.

For example, i want to create a state of some sort wherein the player is restrained and cannot interact with most objects. Another example is armor giving the player stats. So, how would I do this?

Thank you in advanced for any help on this.

I don’t know how familiar you are with TADS 3 syntax and concepts, since you are a newcomer. I’m going to assume you know how to override a method or define a property on an object, but if you don’t and what I say below seems to make no sense, then I suggest you first run through a tutorial to get acquainted with the basic stuff. The usual places to start are “Getting started in TADS 3” and/or “Learning TADS 3” by Eric Eve.

Your request sounds broad and seems to cover quite different topics, which I’ll address separately.

In order to restrict most actions under certain circumstances, the most straightforward solution would be to override the beforeAction method on the actor who’s being restricted, or the roomBeforeAction on the room where the restriction happens. You’ll see examples of that in chapter 4, section “Other Responses to Actions” of “Getting Started in TADS 3” and in section 13.4 of “Learning TADS 3”. In those methods, you can define any condition that’s appropriate for the action being prevented. I don’t know if you are familiar with Inform 7, but from my own limited knowledge of Inform 7, I believe those two methods are the closest equivalent of “instead” rules in Inform, if that means anything to you.

The beforeAction / roomBeforeAction approach suffers the drawback of requiring you to list explicitly all actions that are being allowed (or all actions that are being forbidden, depending on what is easier). This may be entirely sufficient for your needs, but if a list of explicit actions looks like it might be too cumbersome to create or maintain (because you’re afraid of missing some, or of fogetting to update the list as you add new custom actions to your game), then there are other approaches, which consist for example in vetoing all actions that require touching something that’s outside of the actor’s inventory, or outside of the actor’s immediate enclosing location (if he’s locked in a cage or tied to a chair or something of this sort). This is a more advanced approach – I’m not sure it’s documented anywhere outside of the library reference or the library code itself, but it’s feasible. I can elaborate if it sounds useful, but try to get familiar with the beforeAction approach first.

Here, an example is probably most enlighting. Copy-paste the following in a starter game; this should give you a complete mini-game that uses armor stats. Try to walk from one room to another; you’ll get a report of your current armour level and a failure message if you can’t cross the wall of fire because you armour level isn’t high enough. Hopefully, the code is self-explanatory enough and illustrates various things you might want to do with stats.

/*
 *   The superclass of any item that increases an actor's armor level when
 *   worn.
 */
class ArmoredItem: Wearable
    
    /*
     *   The increase in armor level that this item provides.
     *   The default value is 5, but you can override it in actual objects.
     */
    armorLevel = 5
;

class ArmoredActor: Actor
    
    /*
     *   The intrinsic armor level of the actor, i.e., the armor level he has
     *   when he's not wearing any armored item. This is a writable property;
     *   update it to any desired value in the course of the game.
     *   The default is 0, but you can override the start value in specific
     *   actors.
     */
    intrinsicArmorLevel = 0
    
    /*
     *   The total armor level of the actor, including what he's wearing. This
     *   is a computed property; don't override it explicitly.
     */
    totalArmorLevel
    {
        /* Start with the intrinsic armor. */
        local armorLevel = intrinsicArmorLevel;
        /* Add the armor of any armored item we're wearing. */
        for (local item in contents)
            if (item.ofKind(ArmoredItem) && item.isWornBy(self))
                armorLevel += item.armorLevel;
        /* Return the sum. */
        return armorLevel;
    }
;

/* The start room. */
mineEntrance: Room 'Mine entrance'
    "This is the mine entrance. A tunnel leads east. "
    
    east = wallOfFire
;

/*
 *   Make the player derive from ArmoredActor so that he inherits the armor
 *   stats.
 */
+ me: ArmoredActor
    intrinsicArmorLevel = 1
;

/* A helmet that boosts an actor's armor level when worn. */
+ hornedHelmet: ArmoredItem 'horned helmet' 'horned helmet'
    armorLevel = 2
;

/* A suit of armor that boosts an actor's armor level when worn. */
+ shinySuitOfArmor: ArmoredItem 'shiny suit/armor' 'shiny suit of armor'
;

/* A potion that boosts an actor's intrinsic armor level when drunk. */
+ oysterFlavoredPotion: Thing
    'oyster flavored flavoured oyster-flavored oyster-flavoured potion'
    'oyster-flavored potion'
    
    /* Specialize the Drink action. */
    dobjFor(Drink)
    {
        /*
         *   Verify handler. We don't do anything there, which means the action
         *   is allowed unconditionally, provided the normal preconditions are
         *   met (i.e., the potion is held).
         */
        verify() { }
        
        /* Action handler. */
        action()
        {
            /* Make the potion disappear. */
            moveInto(nil);

            /* If the actor has armor level stats, boost it. */
            if (gActor.ofKind(ArmoredActor))
            {
                gActor.intrinsicArmorLevel += 3;
                "{Your/his} armor level just increased permanently by 3
                points. ";
            }
        }
    }
;

/* A second room. */
edgeOfPit: Room 'Edge of Pit'
    "You are at the edge of a dark pit. A tunnel leads west. "
    
    west = wallOfFire
;

/*
 *   A connector between the two rooms that requires a certain armor level to
 *   pass.
 */
wallOfFire: RoomConnector 
    /* The two rooms connected by this connector. */
    room1 = mineEntrance
    room2 = edgeOfPit
    
    /* The armor level required by this connector. */
    requiredArmorLevel = 10

    /*
     *   Define the conditions for travel: the actors must have a sufficient
     *   armor level.
     */
    canTravelerPass (traveler)
    {
        /*
         *   Look for an insufficiently armored actor who's attempting to
         *   travel. If we find none, we're good.
         */
        return findInsufficientlyArmoredActor(traveler) == nil;
    }
    
    /*
     *   In the most general case, the traveler might be a vehicle carrying
     *   several actors. This helper method picks a particular actor aboard the
     *   traveler that is insufficiently armored to cross this connector.
     *   If all actors have sufficient armor, it returns nil. If the traveler
     *   is just a walking actor, then the return value is either the actor
     *   (if insufficiently armored) or nil. A game that uses no vehicle or
     *   works under the assumption that the travelling actor is always the
     *   gActor might not need this complexity.
     */
    findInsufficientlyArmoredActor(traveler)
    {
        return traveler.getTravelerActors().valWhich(
            {a: a.ofKind(ArmoredActor)
            && a.totalArmorLevel < requiredArmorLevel});
    }

    /* If travel is disallowed, explain why. */
    explainTravelBarrier(traveler) 
    {
        local actor = findInsufficientlyArmoredActor(traveler);
        "A wall of fire stands in the way. Walking through it unharmed
        requires an armor level of <<requiredArmorLevel>>, but
        <<actor.theName>> only <<actor.verbToHave>> armor level
        <<actor.totalArmorLevel>>. "; 
    } 
; 

Funny story. After writing that post I actually started reading Learning TASD 3, before then I didn’t know how methods operated. Methods made things make a bit more sense. Although I’m not sure what you mean by overriding methods, unless it’s no different from overriding properties. Definitely going to keep this on had for a while though.

This would definitely be preferred. I was actually trying to do this with my limited knowledge of TADS3. it went ok but I didn’t get the results I wanted. I’ll also try out the beforeAction stuff while I await your reply, sounds good to know.

As for the armor example, that should really help get the ball rolling on some of the equipment stuff I want to do later. Thanks a lot for that. One question though, in the ArmoredActor class you defined what you called a “computed property” I can’t seem to find documentation on that. How does that work? How does it differ from using a method? Also in the for section what would I actually enter?

Also, I ran into another issue recently involving player input outside of actions. I want to be able to prompt the player with a y/n prompt or a prompt asking for an int, but I can’t seem to find anything on how to. The closest I’ve come is inputManager.getInputLine(nil, nil).
One more thing, I have an Event list I’m trying to run that doesn’t seem to activate when it should. Here’s the code I wrote for it. it’s attached to an object.

+Mirror: Fixture 'mirror' 'mirror'
    "to be added later"
    beginGameMirrorEvent: EventList
    {
        eventlist =
                    [
                        'testing the event list.\b',
                        'more event list\b'
                    ]
    }
    dobjFor(Examine){
        action(){
            beginGameMirrorEvent.doScript();
        }  
    }
;    

Thanks again for all the help.

Have a look at the thread “Is user input possible?” from last November; someone asked something similar. There you’ll see a structured way of displaying a particular input prompt when you want to bypass the regular command parsing. You’ll still have to process the input response by yourself though: if the player types " 23 ", you’ll get as a result the string ’ 23 ’ unprocessed. If it’s an int you want, you’ll have to strip whitespace and convert the resulting string to an int, which is a different topic.

Your eventlist property should be eventList, capital-L. TADS is case-sensitive.

Indeed, a method is just a particular kind of property: one that is defined by a block of code which is executed when the property is invoked. Overriding a property or method means redefining it in a particular class or object to replace the version that was defined in the base class. In your own Mirror object, your action() { … } handler is a method that overrides the version that is defined in the base Thing class, so that examining the mirror does something different from examining other Things. In contrast, the reason why your eventlist property doesn’t appear to work is that it fails to override the eventList property in the base EventList class; it only defines another, different property - with the result that your particular event list does not do anything different from the base EventList class (which does nothing).

It does not. What I called informally a “computed property” is actually a method with the empty pair of parentheses omitted so that it superficially looks like a property that simply stores a value. Apart from the syntactic convenience of omitting empty pairs of parentheses, which TADS 3 lets you do for any method that takes no arguments, there is no difference from a regular method. This syntactic convenience gives you the freedom to change the implementation of your properties any time, without impact on the rest of your code. You may for example decide at some point that the armorLevel property of the hornedHelmet in my armor example code should be a method rather than a property that stores a value, because the level of protection provided by the helmet depends on some external factor (say, whether the helmet has been blessed by a spell of some sort) that needs to be re-evaluated every time the property is invoked. It would be inconvenient, just because the helmet’s armor level needs to become a method, to have to add empty pairs of parentheses everywhere the armorLevel property is invoked; unfortunately TADS 3 doesn’t force you to do so, not even in the definition of the method itself.

I’m sorry; I don’t see what you mean here.

This would definitely be preferred. I was actually trying to do this with my limited knowledge of TADS3. it went ok but I didn’t get the results I wanted.
[/quote]
I’ll come back to this later.

I was talking about the line “for (local item in contents),” is “local item in contents” actually valid? This could be that I just don’t understand for statements in TADS3 yet.

Otherwise thanks for the information. I’ll look over all that today and await your reply on the last question. If anything else comes up I’ll reply again.

Yes, it’s valid. It uses the “for (local variable in list)” form of a for loop, which iterates over the contents of a list. “contents” is a property that provides the list of objects being contained in the object on which it is invoked (in this case, the armored actor).

Here is some example code that illustrates what I had in mind. Copy that in a sample game, run it, and experiment with various things like touching, putting, or throwing objects outside of the cage. Here the actions being restricted aren’t listed explicitly; it’s the act of moving large objects out of the cage between the bars or generally reaching out of the cage that is being vetoed.

modify Actor
 
    /*
     *   Restrict actors’ abilities to touch or move things outside
     *   of the cage when they are inside it.
     */
    checkMoveViaPath(obj, dest, op)
    {
        /*
         *   Disallow the touch or move if the object wanting to touch
         *   or being moved is inside of the cage and the target is
         *   outside of the cage. Make an exception for the marble,
         *   which is small enough to pass between the bars.
         */
        if (obj.isIn(cage) && !dest.isOrIsIn(cage) && obj != marble)
        {
            return new CheckStatusFailure(
                '\^<<obj.theName>> cannot get out of the cage;
                the bars are too tight. ');
        }
       /* If we aren’t in the restricted conditions, do the normal checks. */
        else return inherited(obj, dest, op);
    }
;
 
modify playerActionMessages
 
    /*
     *   This is tangential to the main concepts this code snippet attempts
     *   to illustrate, but change the report message to something more
     *   aesthetically pleasing when a thrown projectile is blocked by the
     *   bars of the cage.
     */
    throwFallMsg(projectile, target)
    {
       /* Change the message when the target is the cage. */
        if (target == cage)
        {
            gMessageParams(projectile, target);
            return '{The projectile/he} hit{[s]|} the bars and fall{[s]|} in
            the cage. ';
        }
        /* Otherwise, use the normal message. */
        else return inherited(projectile, target);
    }
;
 
startRoom: Room 'Start Room' "This is the start room. ";

/* Some scenery and projectiles to experiment with. */
 
+ table: Heavy, Platform 'table' 'table';
 
+ cage: Booth 'cage' ' cage';

/* Place the actor in the cage. */
++ me: Actor;
 
++ bowlingBall: Thing 'bowling ball' 'bowling ball';
 
++ marble: Thing 'marble' 'marble';

The central concept here is the checkMoveViaPath method, which decides whether a movement is possible or not. I’m not aware that it’s documented anywhere outside of the library reference manual (as part of the Thing documentation), which makes it a fairly obscure, or advanced, feature.

A caveat, though: this example may give the false impression that this kind of approach is definitely superior to the beforeAction approach of listing the restricted actions explicitly, but the simplicity of the cage situation may be misleading. Things become more complex if, for example, the player is tied to a chair. There you need to worry not only about touchability, but also about vetoing actions that require an object to be simply held or worn (such as, for example, dropping things or wearing or taking off pieces of clothing), because TADS 3 considers holding, touching, and wearing as distinct and fairly independent conditions. Another special case is jumping, which you can’t do when tied to a chair even though it doesn’t involve manipulating objects.

For sufficiently complex restricted situations, it’s not that clear cut whether it’s more practical to think in terms of explicit actions to be forbidden, or in terms of the possible conditions to be vetoed, or a combination of both. Either way, you can’t escape a careful review of the possible situations you may want to restrict. Do what you feel most proficient with, or what occurs most naturally to your mind.

Hello I’m back. I’ve been reading and playing around with this stuff with a bit and I just have a couple of questions.

First, you mentioned turning a readMainCommand string to int. How do I do that?

Second, can modify be called in objects? for example, say I want to modify the Actor class with an ActorState. Can I do that? Also, I am trying so make it so that the only object exempt from the checkViaMovePath is the source of the restraint (as defined in the restraint state) but it’s not finding the object. Here’s the relevant code:

class CharacterStatusState: ActorState
/*just my own child of actor state in case I want to add some extra properties later*/
;

isRestrained: CharacterStatusState
    /*This value is set by the restraint or another method when the restraining occurs*/
    restrainingObject = nil
    /*using this method*/
    assignRestrainingObject(newObject){
        restrainingObject = newObject;
    }
;

gameMain: GameMainDef
{
    initialPlayerChar = me
        showIntro()
    {
        /*just setting the state and the causing obj at start of game*/
        me.setCurState(isRestrained);
        isRestrained.assignRestrainingObject(Shackles);
    }
}

modify Actor
    {
        checkMoveViaPath(obj, dest, op)
        {
            if (gActor.curState == isRestrained && obj != isRestrained.restrainingObject)
            {
                return new CheckStatusFailure(
                    'You cannot do that right now; 
                    you are restrained by <<isRestrained.restrainingObject>>.');
            }
            else return inherited(obj, dest, op);
        }
        beforeAction(){
            if (gActor.curState == isRestrained && obj != restrainingObject && gActionIs(Examine)){
                "<<gDobj.notCloseToExamineMsg>>";
                exit;
            }
        }
    }

modify Thing
    notCloseToExamineMsg = 'You\'re not close enough to examine that'
;

So yeh, the restrainingObject isn’t actually being pulled by everything under the modify, which is strange considering that I’ve tested restrainingObject before and it works just fine normally. Also, again, is it possible to collapse all of the modify actor and thing code under the isRestrained state? that’d be really nice.

TADS 3 has a toInteger function that converts various kinds of values to ints. It’s documented here:

tads.org/t3doc/doc/sysman/tadsgen.htm

together with a bunch of other conversion and data manipulation functions. You’ll find other string manipulation methods being documented in the section about the String intrinsic class in the same manual.

I’m not sure what you mean by that, but I’m under the impression that you’d like to centralise all the code specialised for action restriction (such as checkMoveViaPath, beforeAction, and notCloseToExamine) in your CharacterStatusState class or its isRestrained instance (which sounds like good design). The way I’d do that is to leave those methods where they currently are, in Actor and Thing, but instead of implementing the behaviour there directly, I’d have those methods call other methods defined on the actor state (after checking that the actor state is of the kind that actually defines those delegatee methods). The delegatee methods on the actor state would be the ones that actually carry out the implementation. In fact, if you look at how the library defines the ActorState mechanism, that’s exactly how it proceeds: every method defined in ActorState duplicates a method defined directly on Actor. The version defined on Actor just checks the ActorState and calls the corresponding method on it.

I haven’t tried your code, but the first question that comes to mind is, are you sure that it is obj rather than dest that should be compared to your restrainingObject? The general intent of this checkMoveViaPath method is to check some sort of movement from obj to dest, which means that at least in a number of relevant cases, obj will be the actor itself and dest will be the object the actor attempts to reach. If I understand your goal correctly, you’d like the actor to be allowed to reach the restraining object and disallow reaching anything else, so it’s the dest argument that should be discriminated against.

I’ve tried that. The problem is that it seems to not be converting my string correctly. For example, I have this code:

Event(){
    "Now attempting to convert string to int. enter a integer value.\b";
    local intString = readMainCommand(rmcCommand);
    toInteger(intString);
    intString += 1;
    "Your number added by 1 is <<intString>>";
}

And it works, except for one thing: if I input, say, 1, at the end I return 11. For some reason the toInteger operation always returns what I want multiplied by 10. I could just divide the number by ten once it’s converted, but I’d like to know if this can be solved without that.

What I meant was something like this:

isRestrained: CharacterStatusState
    modify Actor
    {
        /*body of modify*/
    }
;

However, I have no issues doing what you have stated, but I can’t get that to work either. I’ve modified my code to how I understand what you’re saying:

 modify Actor
    activateRestrainedState()
    {
        checkMoveViaPath(obj, dest, op)
        {
            if (gActor.curState == isRestrained && dest != isRestrained.restrainingObject)
            {
                return new CheckStatusFailure(
                    'You cannot do that right now; 
                    you are restrained by <<isRestrained.restrainingObject.getObjectName()>>.');
            }
            else return inherited(obj, dest, op);
        }
        beforeAction(){
            inherited;
            if (gActor.curState == isRestrained  && gDobj != restrainingObject && gActionIs(Examine)){
                "<<gDobj.notCloseToExamineMsg>>";
                exit;
            }
        }
    }
;

Here the compiler seems to think that I am trying to execute a method when I am trying to define it. It just keeps giving a set of two semi colon errors and I don’t understand why they’re appearing. As far as I know I should be able to do this, but I can’t figure out why it’s not working.

This solves half my problem because now checkMoveViaPath is actually recognizing when the object I am using is the correct one. However, I have a particular line that still does not work. I’m trying to get the name of the object restraining the player when the action is vetoed by checkMoveViaPath, but I can’t find anything that works.

The toInteger function doesn’t convert the variable in place; it returns an integer value which you need to assign explicitly to a variable. Try this:

local intVal = toInteger(intString);
intVal += 1;

That won’t work. You can’t modify a class or object conditionally like this. The best way to think about “modify” is as a way to substitute a new class or object for an old one, in your entire code, in such a way that the new one inherits its implementation and its name from the old one (and the old one becomes unnamed). It’s actually no different, internally, from defining a new subclass. This means in particular that you can’t use modify in places where you couldn’t define a new class or object.

No, I didn’t mean defining a function inside another. (You can’t, at least not in this manner and with the effect you want, and the compiler indeed thinks that you are attempting to call a function inside the definition of another, and complains about a missing semicolon after what it interprets as a function call.)

What I meant (sketchily) was something like this:

modify Actor
    checkMoveViaPath(...)
    {
        if (curState == isRestrained) curState.checkMoveViaPath(...);
        else inherited(...);
    }
;

isRestrained: CharacterStatusState
    checkMoveViaPath(...)
    {
        /* do the actual check here */
    }
;

which is pretty similar to the pattern the library follows to delegate many Actor method implementations to the current actor state.

Alternatively, instead of checking for the isRestrained state specifically in the Actor code, you might want to use ofKind to check for a particular subclass of ActorState, if you anticipate that you may need to define several actor states (all deriving from that subclass) that want to handle the checkMoveViaPath implementation.

Use isRestrained.restrainingObject.theName . restrainingObject alone isn’t printable; you need to invoke one of its name properties to get a string that can be printed. There are several: theName, aName, and a bunch of others, giving several variants of the name of the object.

Ok, so I tried what you suggested, and it seems to have created a new problem. When I attempt to do an action, say opening something, checkMoveViaPath returns a “nil object reference” and crashes.

Here is the current code

modify Actor
    checkMoveViaPath(obj, dest, op){
        if (ofKind(CharacterStatusState)){
            curState.checkMoveViaPath(obj, dest, op);
        }
        else inherited(obj, dest, op);
    }
    beforeAction(){
        inherited;
        if (ofKind(CharacterStatusState)){
            gActor.curState.beforeAction();
        }
    }
;

isRestrained: CharacterStatusState
    /*This value is set by the retraint or another method when the restraining occurs*/
    restrainingObject = nil
    /*using this method*/
    assignRestrainingObject(newObject){
        restrainingObject = newObject;
    }
    checkMoveViaPath(obj, dest, op)
    {
        if (dest != isRestrained.restrainingObject)
        {
            return new CheckStatusFailure(
                'You cannot do that right now; 
                you are restrained by <<isRestrained.restrainingObject.theName()>>.');
        }
        else return inherited(obj, dest, op);
    }
    beforeAction(){
        inherited;
        if (gDobj != restrainingObject && gActionIs(Examine)){
            "<<gDobj.notCloseToExamineMsg>>";
            exit;
        }
    }
;

Actually I’m somewhat puzzled that your current code does anything at all, because you invoke ofKind on the actor rather than on the actor state. (This is implicit: when you invoke ofKind without a prefix, the prefix defaults to self, which in your code represents the actor.) So what I would expect is that the ofKind test would always fail, and that the inherited handling would always run, with no obvious effect.

But considering that what I think should happen does not actually happen… Could it be that the curState property is set to nil at the time the line curState.checkMoveViaPath executes? That would explain the nil reference. You can only invoke a method on a true object so if a variable or property is set to nil when you invoke a property on it that will fail. You should test explicitly that the variable is not nil.

As an aside, it’s sometimes tricky to figure out the cause of an error from the error message alone, but if you’re using Workbench on Windows, running your game with the debugger helps a lot. Do you know how to do that? Even I f you don’t have Windows it’s still possible to run your game in debug mode and get a full stack trace (that is, a full listing of which method called which other method and at what line number the program encountered the error), which is also helpful.

fixed that, stull nothing. the nil object in that instance was caused by the “else inherited(obj, dest, op);” line, it needed to be “else return inherited(obj, dest, op);” to work. Still didn’t solve my issue because once I fixed the curState.ofKind the same happened

checked that. isRestrained is set just fine. Then I started testing this checkMoveViaPath method, and it refuses to work if you pass it through another method. for example, I tried this:

modify Actor
    checkMoveViaPath(obj, dest, op){
        exampleMethod(obj, dest, op);
    }
    exampleMethod(obj, dest, op){
        return inherited(obj, dest, op);
    }
;

I also tried something like this

modify Actor
    checkMoveViaPath(obj, dest, op){
        local objA = obj;
        local destA = dest;
        local opA = op;
            exampleMethod(objA, destA, opA);
    }
    exampleMethod(objB, destB, opB){
        return inherited(objB, destB, opB);
    }
;

Nothing. I just can’t get checkMoveViaPath to allow its return to be stored in another method.

here’s the full stack:

me.canTouchViaPath(me, Coffer, PathFrom) + 0x11 <---- this one is marked as the failure (note that there is no nil value)
func#69188(me, PathFrom) + 0x1F
me.traversePath([me,PathPeer,Coffer], obj#21a3 (AnonFuncPtr)) + 0x2A
me.selectPathTo(Coffer, &canTouchViaPath) + 0x61
me.getTouchPathTo(Coffer) + 0x32
me.canTouch(Coffer) + 0x57
touchObj.verifyPreCondition(Coffer) + 0x1A
func#d387() + 0x69
Action [225f].withVerifyResults(obj#21c8 (VerifyResultList), Coffer, obj#21c9 (AnonFuncPtr)) + 0x2D
Action [225f].callVerifyProp(Coffer, &verifyDobjOpen, &preCondDobjOpen, &remapDobjOpen, nil, DirectObject) + 0xAD
Action [225f].getSortedVerifyResults([obj#21e3 (ResolveInfo)], &verifyDobjOpen, &preCondDobjOpen, &remapDobjOpen, DirectObject, obj#225a (qualifiedSingularNounPhrase(definite)), 1) + 0x4B
Action [225f].filterAmbiguousWithVerify([obj#21e3 (ResolveInfo)], 1, &verifyDobjOpen, &preCondDobjOpen, &remapDobjOpen, DirectObject, obj#225a (qualifiedSingularNounPhrase(definite))) + 0x33
TAction [225f].filterAmbiguousDobj([obj#21e3 (ResolveInfo)], 1, obj#225a (qualifiedSingularNounPhrase(definite))) + 0x22
func#570a7() + 0x24
withParserGlobals(me, me, obj#225f (predicate(Open)), obj#21dc (AnonFuncPtr)) + 0x40
Resolver [225f].withGlobals(obj#21dc (AnonFuncPtr)) + 0x1D
Resolver [225f].filterAmbiguousNounPhrase([obj#21e3 (ResolveInfo)], 1, obj#225a (qualifiedSingularNounPhrase(definite))) + 0x30
DefiniteNounProd [225a].resolveDefinite(ResolveAsker, ‘coffer’, [obj#21e3 (ResolveInfo)], obj#225a (qualifiedSingularNounPhrase(definite)), obj#225f (predicate(Open)), obj#224e (CommandRanking)) + 0x2B
DefiniteNounProd [225a].resolveNouns(obj#225f (predicate(Open)), obj#224e (CommandRanking)) + 0x27
LayeredNounPhraseProd [225b].resolveNouns(obj#225f (predicate(Open)), obj#224e (CommandRanking)) + 0x13
LayeredNounPhraseProd [225c].resolveNouns(obj#225f (predicate(Open)), obj#224e (CommandRanking)) + 0x13
LayeredNounPhraseProd [225d].resolveNouns(obj#225f (predicate(Open)), obj#224e (CommandRanking)) + 0x13
nounList(nonTerminal) [225e].resolveNouns(obj#225f (predicate(Open)), obj#224e (CommandRanking)) + 0x13
TAction [225f].resolveNouns(me, me, obj#224e (CommandRanking)) + 0x20
CommandProdWithDefiniteConj [2260].resolveNouns(me, me, obj#224e (CommandRanking)) + 0x14
FirstCommandProd [2261].resolveNouns(me, me, obj#224e (CommandRanking)) + 0x14
CommandRanking [224e].calcRanking([me,me]) + 0x18
CommandRanking.sortByRanking([obj#2261 (firstCommandPhrase(commandOnly))], me, me) + 0x32
executeCommand(me, me, [[‘open’,tokWord,‘open’],[‘coffer’,tokWord,‘coffer’]], true) + 0x149
PendingCommandToks [2269].executePending(me) + 0x1D
me.executeActorTurn() + 0xD4
func#20800() + 0x17
senseContext.withSenseContext(nil, sight, obj#236f (AnonFuncPtr)) + 0x1D
callWithSenseContext(nil, sight, obj#236f (AnonFuncPtr)) + 0x15
func#20841() + 0x3F
withCommandTranscript(CommandTranscript, obj#2374 (AnonFuncPtr)) + 0x49
withActionEnv(EventAction, me, obj#2374 (AnonFuncPtr)) + 0x3C
me.executeTurn() + 0x30
runScheduler() + 0xB6
runGame(true) + 0x2C
gameMain.newGame() + 0x1B
mainCommon(&newGame) + 0x4D
main([‘Unnamed Adventure Game_dbg.t3’]) + 0x1B
flexcall(main, [‘Unnamed Adventure Game_dbg.t3’]) + 0x59
_mainCommon([‘Unnamed Adventure Game_dbg.t3’], nil) + 0x53
_main([‘Unnamed Adventure Game_dbg.t3’]) + 0x12

and this is the line in thing.t that supposedly crashed:

canTouchViaPath(obj, dest, op)
        { return checkTouchViaPath(obj, dest, op).isSuccess; }

In fact it looks like the same error as the one you just spotted: your call to exampleMethod should be preceded with “return”. A method that doesn’t explicitly return anything implicitly returns nil.

That makes tons of sense now that I think about it. Just added return to the original and it seems like it’s almost solved; there’s just one more problem: the “else return inherited(…)” line in isRestricted seems to be returning nil for some reason. the same line in modify Actor seems to do fine, so it must be how it’s traveling from method to method. Although, I can’t see why it wouldn’t work, isRestricted.checkMoveViaPath should in this instance be returned as inherited(…) which should come out in the original function as return inherited(…). Everything else in the function works fine now btw. Here’s the relevant code just in case it matters.

modify Actor
    checkMoveViaPath(obj, dest, op){
        if (curState.ofKind(CharacterStatusState)){
            return curState.checkMoveViaPath(obj, dest, op);
        }
        else return inherited(obj, dest, op);
    }
    beforeAction(){
        inherited;
        if (ofKind(CharacterStatusState)){
            gActor.curState.beforeAction();
        }
    }
;

isRestrained: CharacterStatusState
        /*This value is set by the retraint or another method when the restraining occurs*/
    restrainingObject = nil
    /*using this method*/
    assignRestrainingObject(newObject){
        restrainingObject = newObject;
    }
    checkMoveViaPath(obj, dest, op)
    {
        /*
         *   determines if an item is in reach (i.e. only the restraining
         *   object) and restricts movement.
         */
        if (dest != restrainingObject)
        {
            return new CheckStatusFailure(
                'You cannot do that right now; 
                you are restrained by <<isRestrained.restrainingObject.theName()>>.');
        }
        else return inherited(obj, dest, op);
    }
    beforeAction(){
        inherited;
        if (gDobj != restrainingObject && gActionIs(Examine)){
            "<<gDobj.notCloseToExamineMsg>>";
            exit;
        }
    }
;

The else return inherited(…) is ok in Actor, but should not be in CharacterStatusState since it is ActorState which doesn’t have checkMoveViaPath so can’t inherit that from parent class. You probably just want to else return checkStatusSuccess;

Thanks, figured it had to do with something like that, I just didn’t know how to fix it. Well, that should be all I need. Thanks again everyone for helping.

Hey, I’m back because I just found a new problem that has to do with all of this stuff we’ve been talking about here.

Simply put, I can’t change the actor’s location.

Every time my char attempts to move to a new room I get an invalid object ref error at this line in actor.t:

 getActor()
    {
        if (location.ofKind(ActorState))    /*<------ this one*/
            return location.getActor();
        else
            return location;
    }

I know it’s because of my actor states, I tested and found that out, I just don’t know why or how to fix it.

An example of the state on my actor when this occurred could be something as simple as this:

normalState: ActorState
;

So… could I get some help again? It would be appreciated.

ActorStates should be contained in their respective actors using either + syntax or location property, ie.:

me: Actor
;

+ isRestrained: CharacterStatusState
;

// or

isRestrained: CharacterStatusState
    location = me
;

I was about to post the same thing myself :slight_smile:
I believe tomasb meant location = me for this variation though.