NotifyInsert while IsOn is True [SOLVED]

Okay, I’m having a weird problem. I want to have the player change a lightbulb. Since the Flashlight + Battery example in the Learning Tads3 manual seemed virtually identical to the behavior I wanted, I copy-pasted the code, changed “battery” to “lightbulb”, etc., and added the Fixture class:

[spoiler][code]
+overheadLight : Fixture, Flashlight, RestrictedContainer ‘overhead fitting/fixture/lamp/light/socket’ ‘overhead light fixture’
bulkCapacity = 1
validContents = [brokenBulb, lightbulb]
makeLit(stat)
{
if(stat && !lightbulb.isIn(self))
failCheck('Alas, nothing happens. It needs a new bulb. ');
else
{
inherited(stat);
if(stat)
{
finishGameMsg(ftVictory, [finishOptionUndo, finishOptionFullScore]);
}
}
}
notifyInsert(obj, newCont)
{
inherited(obj, newCont);
if(obj == lightbulb && isOn)
{
"As the new bulb is put in, light immediately begins to shine. ";
makeLit(true);
}
}

;
[/code][/spoiler]

But for reasons beyond my understanding, there’s a weird glitch if I try to put the new bulb in while the light is already on. Apparently, it calls the failCheck on makeLit before putting the lightbulb in. I’ve tried over-riding this with a moveInto inside the notifyInsert method, but this results in a path-finding problem and a stack overflow:

If the light is off, both bulbs work fine. If it’s on, the dead bulb works fine. It’s only when putting the new bulb in while the light is on that trouble happens. I get my failCheck message, as mentioned, and when I look in my inventory, the character is still holding both bulbs. What is happening, and how can I fix it? :frowning:

(The thought did occur to side-step the issue by making the player turn the light off, first, but that seems like a petty annoyance to inflict.)

Not sure what you mean by “the dead bulb works fine.” I’m also wondering whether the overheadLight is switchable, because I don’t understand what you mean by “when putting the new bulb in while the light is on.” How can the light be on if there’s no bulb in it? It can be switched on, but it can’t be on, if you see what I mean.

The overheadLight object, if it’s switchable, can be in any of six states (no bulb, dead bulb, good bulb, each of them while the object is switched on or off). There are quite a lot of transitions between states that you need to handle. For instance, putting the dead bulb in while the good bulb is in (which should cause an implicit Take of the good bulb) but the light is switched off. The code you’ve given us doesn’t deal with most of those transitions. If this is the entire code for the object, I’d say you need to think more about the states and transitions between them.

I’m afraid I don’t fully understand what you’re getting at. The overheadLight, as far as I know, can be turned on or off, and either lit or not; that’s only four states, at most. The broken bulb is not meant to do anything – when I say it works, I mean I can insert it into the overheadLight while the overheadLight is switched on, and the object will move into its new location just fine. The new bulb, for some reason, will not move into the overheadLight while the thing is switched on, which in turn (I think) prevents it from being Lit.

As for transitions…you’ve lost me, I’m sorry. As far as I know, it should be a matter of “if good bulb is in self, make Lit when On.” What’s missing from the code?

ETA: As far as automatically taking the old bulb out when the new one is put in; I hadn’t actually considered it, true, but I figure that’s a nicety that I can work on once the main functions start working.

Wouldn’t make a difference when you define your object as Flashlight, RestrictedContainer, Fixture instead of Fixture, Flashlight, RestrictedContainer?

I can tell you what’s wrong … but fixing it is not really going to make your light fixture fully functional. I’ll fiddle with the code for a while.

What’s wrong is this: notifyInsert is called at a point BEFORE the obj is added to the contents of newCont. notifyInsert does not actually DO the inserting – it just allows the container to reject the attempt. So at the point when notifyInsert calls makeLit, the new bulb is not in the light fixture. At that point, failCheck causes the attempted action to exit. The PutIn action is never called, because failCheck has shut off your intended functionality.

I had a look at the Flashlight class, and fiddled with it a little (see below). Considering the states of the overhead light fixture as a whole, it can be in any of the following:

switched on, no bulb
switched off, no bulb
switched on, dead bulb
switched off, dead bulb
switched on, good bulb
switched off, good bulb

So there are two transitions you need to handle: (1) turning on the light when the good bulb is already there, and (2) inserting the good bulb when the light fixture is already switched on. If you’re not going to make that the game-winning action, then you also need to handle the transitions (3) turning off the fixture while the good bulb is in it, and (4) removing the good bulb while the fixture is switched on.

You were trying to use makeLit, but that seemed inadvisable, so I used makeOn instead:

[code]startRoom: Room ‘Start Room’
"This is the starting room. There’s a light hanging from the ceiling. "
;

  • me: Actor
    ;

++ brokenBulb: Thing ‘broken (light) bulb’ ‘broken light bulb’
"It’s kind of gray along one side, and rattles when you shake it. "
;

++ lightbulb: Thing ‘new (light) bulb/lightbulb’ ‘new light bulb’
"60 watts – looks good. "
;

+overheadLight : Fixture, Flashlight, RestrictedContainer ‘overhead fitting/fixture/lamp/light/socket’ ‘overhead light fixture’
"The light is hanging from the ceiling. "
bulkCapacity = 1
validContents = [brokenBulb, lightbulb]
makeOn(stat) {
isOn = stat;
if (stat && lightbulb.isIn(self)) {
makeLit(stat);
finishGameMsg(ftVictory, [finishOptionUndo, finishOptionFullScore]);
}
else {
if (stat) failCheck('Alas, nothing happens. Looks like it needs a new bulb. ');
else {
if (lightbulb.isIn(self)) "You switch out the light, making the room a bit darker. ";
else "Click. (Nothing much happens.) ";
}
}
}
iobjFor(PutIn) {
action() {
inherited;
if ((gDobj == lightbulb) && isOn)
finishGameMsg(ftVictory, [finishOptionUndo, finishOptionFullScore]);
}
}
;[/code]

I’m not using notifyInsert at all, because that’s primarily intended either to reject an action or to cause side effects. Here, we want the insertion to proceed, so the relevant stuff needs to be put in iobjFor(PutIn) action().

@TomasB - I’d thought of that, but shuffling the class orders around didn’t do anything.

@Jim -OMG! Thank you! This was driving me nuts. :slight_smile: