Some Problems with a Openable, Booth

Hello TADS community!

I’m currently in the process of learning TADS 3 and maybe a little bit of jumping the gun with my question. Somewhere in the middle of Heidis further adventures from the Getting Startet tutorial I came up with a seemingly simple idea… Maybe someone feels inclined having a look at it and can make a suggestion or two.

Here is the idea: Inside the little cottage Room is a closet, implemented as a Openable Booth. Heidi can enter the closet, close the door of it and reopen it again.

+ closet : Openable, Booth 'pretty little wooden closet' 'little closet'
    "It's a wooden closet, large enough to hide inside in case of an emergency. "
	initSpecialDesc = "A simple wooden closet stands here. "
	dobjFor(Open)
	{
		verify() {
			if (self.isOpen())
				illogicalAlready('The closet is already open.');
			else
				inherited;
		}
	}
	dobjFor(Close)
	{
		verify() {
			if (!self.isOpen())
				illogicalAlready('The closet is already closed.');
			else
				inherited;
		}
	}
;

Problems:

  1. Closing the closet door from inside (it gets ‘pitch black’) and then reopen the door brings up the full description of the enclosing room. How can I suppress that description after issuing a dedicated description "You open the closet door, letting some light in. " in the dobjFor(Open) part of the closet?
  2. While standing in the closet, the description of the enclosing room contains the closet I am looking out of. How can I eliminate this item from the description of the enclosing room while beeing in it?
  3. While beeing in the closet I can close the front door (of the cottage) without stepping out of the closet. The straightforward approach of making Open and Close for the front door illogical from inside the closet works, but that could get quite messy when more and more objects and actions appear in the room. Is there a ‘smart’ way of prohibiting a whole bunch of actions from within a booth?

I’m well aware, that these questions are probably all answered somewhere in the further tutorials on TADS 3… But I coudn’t find the answers there explicit enough for me to understand. And the problem - in it’s simplicity - is nice, isn’t it?

2 Likes

Once I am back on desktop, I’ll look through the docs. These are all things I had to solve for I Am Prey so it’s 100% doable.

Are you using the Adv3 library or Adv3Lite? I have solved this for Adv3Lite before, but I’m way less knowledgeable in Adv3.

For Adv3Lite, objects are always listed by default when isFixed = nil. You might want to try to TAKE CLOSET, and see if you forgot to make it fixed or not. If it’s not fixed yet, then setting isFixed = true should keep the player from picking it up, and will prevent the closet from being listed by default.

This should be remedied with:

+ closet : Openable, Booth 'pretty little wooden closet' 'little closet'
    // ... other code above...

    // This prevents the player from reaching elsewhere from
    // inside this container.

    allowReachOut(obj) { return nil; }
;

Probably solved by applying this to the closet:
isLit = true

This assures the game that light is found within the closet, and the player won’t be tossed into darkness when the door closes. This might remedy the description repetition issue, but if it doesn’t then this might be one way of overriding the behavior:

+ closet : Openable, Booth 'pretty little wooden closet' 'little closet'
    // ... other code above...

    dobjFor(Open)
    {
        verify() {
            if (self.isOpen())
                illogicalAlready('The closet is already open.');
            else
                inherited;
        }
        report() {
            if (gPlayerChar.isIn(self))
                "Put your alternative opening message here.";
            else
                inherited;
        }
    }
;

If you are using Adv3 instead of Adv3Lite, then I have no idea if any of this will work, lol. But hopefully this solves what you describe. If not, then you know who to visit when exacting your revenge~ :grin:

2 Likes

Once you’ve made your booth fixed and blocked the player from interacting with anything in the room it’s nested within…why not just make it a separate Room?

2 Likes

That’s certainly one solution, but if @TomD is just starting with TADS 3, then I don’t know if connected sensory checks between rooms would be a good deep-end to dive into right now lol. At least I’m assuming the intent is for the player to still see what’s in the enclosing room.

1 Like

Thank you very much for your thoughts on this!

I’m using adv3 for now. All of your suggestions didn’t work out of the box. But they helped me in finding more or less practical solutions.

For the content listing this one works in adv3:

+ closet: Openable, Booth...
  isListed { return !gPlayerChar.isIn(self); }

For the reachability of the front door, a
preCond = [actorDirectlyInRoom]
in the dobjFor(Open...) macros handles the situation in a logical manner triggering the necessary implicit action (stepping first out of the closet).

And for the lighting, a light source inside the closet

++ smartwatch: Wearable 'dimly glowing smart watch/smartwatch' 'smart watch'
    "It's a smartwatch of the Apple kind. "
	brightness = 2
;

indeed prevents the surrounding room to be described again when the closet is opend from the inside. That is not exactly what I had in mind. I’d like to control the description directly, independed of the reappearing light.

But for now I consider this case as solved. I wanted just to fiddle a little bit with the actions before moving on to the second part of the Getting Started tutorial.

Thanks for your kind response Jess:)

2 Likes

That seems to be the best solution for a real game. I’m just exploring the possibilities:)

2 Likes

Sorry I’m late to the party…
The situation you’ve already encountered with NestedRooms is a prominent issue with out-of-the-box adv3. There is indeed a more elegant way to deal with what objects you should be able to reach/interact with from inside closets and the like. Out of the box, it involves making your closet also subclassed from OutOfReach. But a more rounded solution (if you are to have more than one NestedRoom in your game) is to actually modify the NestedRoom class to include certain parts of the OutOfReach behavior. This is actually demonstrated in one of the example games that was provided with the TADS package.
You’re just getting started, so I wouldn’t want you to feel needlessly overwhelmed at the extra complexity, but I can pass you my code block for the said modifications if you are interested in plowing forward here. Otherwise, the quick solution for your closet would be

closet: OutOfReach, Openable, Booth
    canReachFromInside(obj, dest) { 
       return nil; 
       //return dest is in(objReachableFromCloset, reachable2);
    }
;

The cleaner way to implicitly remove the player from the closet (rather than a precondition on the front door object) is

    // this will probably be necessary if OutOfReach is your leftmost superclass
closet:...
   tryImplicitRemoveObstructor(sense, obj) {
       return tryRemovingFromNested();
   }
;
2 Likes

FWIW, recently discussed my tribulations with this problem here. I would echo that the problem is fiddly enough that I go to a room implementation wherever feasible.

2 Likes

Also, I don’t know if I misinterpreted your intent, but you can certainly do things like this:

closet: Openable, Booth
   dobjFor(Open) {
       action {
         "My own message";
         inherited;
   }}
;

A weird trick you can do to hack around inconvenient darkness:

me: Actor
   brightness = (isIn(closet) ? 3 : inherited)

Then you don’t have to introduce extra dummy objects to light things.

1 Like

Not to beat a dead horse (and I surely understand the educational value of pushing to a solution even if suboptimal), but note that a separate Room implementation COULD provide solutions your problems:

These are resolved/managed with remoteDesc() in the adjacent Room (and perhaps a SenseConnector between the two).

Separate Room enforces this naturally.

All that said, other Boothy ways to address these issues that I didn’t see discussed above are:

I often use

    isLookAroundCeiling(actor, pov) { return true; }

inside booths to keep the outer room descriptions from intruding.

This is a quick and dirty way to manage room description perspectives:

outerRoom : Room
    desc = "Outer room description.  <<unless gPlayerChar.isIn(myBooth)>>Oh yeah,
        there\'s a booth in here too.  <<end>>"
    (& etc)
4 Likes

Same on both counts.

I absolutely understand the pedagogical uses of figuring out how to do something a particular way just to figure out how to do it that way (I’ve spent weeks doing that very thing, as documented in numerous other threads).

But when I was doing something similar (player hiding in a bathroom stall, able to hear but not see what was going on in the containing public restroom, lights in the stall going out if the bathroom lights are turned off) I handled it via a separate room (or rather set of rooms) and just stapled on manual tweaks for the specific bits of inter-room overlap I wanted to allow.

In my case I was using my bespoke scene implementation to encapsulate all of the fiddly stuff and non-scene defaults to handle default failure messages (“You don’t hear anything interesting going on outside of the stall.” and so on).

Basically what I usually do is look at how adv3 handles something and whenever I have a special case like this I try to figure out what approach lets me get what I want with the minimum number of manual overrides. Both to make it easier to implement and because I figure if something has more than a couple special cases/conditions to check for then my chances of missing something start going up exponentially.

2 Likes

For remoteDesc() and SenseConnector - that is too complicated for me by now.

That is a good one. I’ll definitively put that in my toolbox!
And the <<unless>> <<end>> thing is also a nice thing that I haven’t seen before. Thanks for the good response JJ!

2 Likes

That is indeed weird, at least from the programmers point of view:D
The thing is, the ‘pitch black’ is not unwanted. Only the automatic room description on opening, i.e. letting light in, was unwanted.
But with the isLookAroundCeiling() method it becomes natural. Seeing the interior of the closet after letting some light in is even better than no description at all.

2 Likes

To wrap things up:

Supressing the full description of an enclosing room after reappearing light can be handled by replacing it with a more appropriate description of the booth we are currently in (thanks @jjmcc):

+ closet: Openable, Booth 'pretty little wooden closet' 'little closet'
	isLookAroundCeiling(actor, pov) { return true; }

Excluding the closet we are standing in from the description of the surrounding room can be done with:

    isListed { return !gPlayerChar.isIn(self); }

Inhibiting “a whole bunch of actions” from within a booth is to vague for a general solution. Something can be done with OutOfReach, remoteDesc() and SenseConnector’s… (Thanks @johnnywz00 , @jjmcc ) For now a simple

preCond = [actorDirectlyInRoom]

for my two objects does the job.

2 Likes

Sorry, misunderstood your intent there!

I know this thread was about adv3, but in case anyone is having issues with booths in adv3lite and comes across this thread, I’ll drop a mention that @Eric_Eve and I have been on a bug-squashing rampage addressing issues with closed booths. So if 2.1.1 is giving you problems, try the git version (or a subsequent release if one exists by the time you read this).

3 Likes