Important! Please read the announcement at http://myst.dustbird.net/uru-account.htm
Also! Please read the retirement announcement at http://myst.dustbird.net/uru-retirement.htm

Curse Of The Neolbah Elevator

From UamWiki

I know, I know. The post title sounds like a cheap "B" horror movie.......and that's just about what this has felt like!

Not too long after Agent, Dustin and I figured out not only how to use Subworlds in Max, but to also get them converted by Drizzle (MOUL uses PhysX and Uru:CC (POTS) uses Havok), I decided to put an elevator in Neolbah.

Now, at first it was working just fine. It was nothing complex, just a platform to go up and down. Not very multiplayer friendly however (no call buttons).

However, something happened as Neolbah grew. The elevators button would not work anymore. The cursor would not go hot when the player moved it over the button.

I checked over and over, but there was nothing wrong, and it was made just like the elevator I have in my test Age, which was still working fine.

I was able to get the button to work sometimes if I deleted the click proxy and made a new one, but that only worked sometimes.

Unfortunately I had to turn away from this as I had another large project I was part of. That too became cursed! We also needed an elevator, and you guessed it: the button wouldn't work in it either.

Near as Dustin and I can tell is that this is either some random bug that happens during export from Max, or with the conversion during Drizzle. However, the problem with that is my test Age. Export it over 1,000 times and it works EVERY time! If it was a random bug, then it should show up in the Test Age once in a while......

Well one of the things I ended up doing that seemed to "cure" the bug, was to increase the size of my Subworld Enter region, so that it encompassed the whole platform. Cyan's Ages normally just use a small (0.5 units thick) region since the act of entering is what triggers moving the avatar to the Subworld. Still, it seems to have fixed that bug.

The other problem I had was trying to make the elevator multiplayer friendly. What you want to do is have an elevator that stays on the floor it was told to go to. But if it does that, you also need to give the player a way to call it up to them.

So using xAgeSDLBoolToggle should work just fine for that, so that the game "remembers' where the elevator was left at. And then just have call buttons set up with responders, right?

Wrong.

I've tried that, and while it sounds good in theory, it just doesn't seem to want to work correctly. I'd drop the elevator down to the basement, use flymode to get back up top. Click on the call button and then wait.....and wait....and wait..........no elevator.

So I'd use flymode again, and the moment I dipped below the floor, the elevator would kick in and start to rise.

Talk about a weird bug!

I think it was at this point I started to picture the Pharoh's Mummy chasing me in Neolbah.........

Okay, so let's go back and look at elevators in Cyan's Ages:

Teledahn,
Gahreesen
Er'cana

Those are great examples. And they all have one thing in common: Python is controlling their elevators.

Teledahn has a 3 floor python file to control it's elevator. Gahreesen has it's own python file for it's elevator, and Er'cana has 2 python files, one for the Bakery platform, and one for the Hopper Car that we ride into Silo A.

So, okay, no problem, right? Let's open up Teledahn's python file:

Note: this is just a clip of it:

from Plasma import *
from PlasmaTypes import *
actSendUp = ptAttribActivator(30, 'Actvtr: Elev Up Btn')
actSendDn = ptAttribActivator(31, 'Actvtr: Elev Down Btn')
actCall1 = ptAttribActivator(32, 'Actvtr: Call Btn #1')
actCall2 = ptAttribActivator(33, 'Actvtr: Call Btn #2')
actCall3 = ptAttribActivator(34, 'Actvtr: Call Btn #3')
respStrain = ptAttribResponder(35, 'Rspndr: Elev Locked Sound')
resp1to2 = ptAttribResponder(36, 'Rspndr: 1 to 2')
resp1to3 = ptAttribResponder(37, 'Rspndr: 1 to 3')
resp2to3 = ptAttribResponder(38, 'Rspndr: 2 to 3')
resp3to1 = ptAttribResponder(39, 'Rspndr: 3 to 1')
resp3to2 = ptAttribResponder(40, 'Rspndr: 3 to 2')
resp2to1 = ptAttribResponder(41, 'Rspndr: 2 to 1')
xrgnDoor1 = ptAttribExcludeRegion(42, 'xRgn: Door #1')
xrgnDoor2 = ptAttribExcludeRegion(43, 'xRgn: Door #2')
xrgnDoor3 = ptAttribExcludeRegion(44, 'xRgn: Door #3')
xrgnOnboardDoorA = ptAttribExcludeRegion(46, 'xRgn: OnboardDoor #1')
xrgnOnboardDoorB = ptAttribExcludeRegion(47, 'xRgn: OnboardDoor #2')
actSubworld = ptAttribActivator(48, 'Actvtr: subworld region')
elevPwrOn = 0
elevLocked = 1
elevCurrFloor = 2
elevIdle = 1
kStringAgeSDLPwrOn = 'tldnWorkroomPowerOn'
kStringAgeSDLElvLocked = 'tldnElevatorLocked'
kStringAgeSDLElvCurrFloor = 'tldnElevatorCurrentFloor'
kStringAgeSDLElvIdle = 'tldnElevatorIdle'
AgeStartedIn = None
class tldn3FloorElevator(ptResponder,):
    __module__ = __name__

    def __init__(self):
        ptResponder.__init__(self)
        self.id = 5011
        version = 7
        self.version = version

and further down:

        global elevLocked
        global elevPwrOn
        if (AgeStartedIn == PtGetAgeName()):
            ageSDL = PtGetAgeSDL()
        else:
            PtDebugPrint('tldn3FloorElevator.OnServerInitComplete():\tERROR -- not in the age we started in?')
            return 
        ageSDL.setFlags(kStringAgeSDLElvIdle, 1, 1)
        ageSDL.sendToClients(kStringAgeSDLElvIdle)
        ageSDL.setNotify(self.key, kStringAgeSDLPwrOn, 0.0)
        ageSDL.setNotify(self.key, kStringAgeSDLElvLocked, 0.0)
        ageSDL.setNotify(self.key, kStringAgeSDLElvCurrFloor, 0.0)
        ageSDL.setNotify(self.key, kStringAgeSDLElvIdle, 0.0)
        try:
            elevPwrOn = ageSDL[kStringAgeSDLPwrOn][0]
            elevLocked = ageSDL[kStringAgeSDLElvLocked][0]
            elevCurrFloor = ageSDL[kStringAgeSDLElvCurrFloor][0]
            elevIdle = ageSDL[kStringAgeSDLElvIdle][0]
        except:
            elevPwrOn = false
            elevLocked = true
            elevCurrFloor = 2
            elevIdle = 1
            PtDebugPrint('tldn3FloorElevator.OnServerInitComplete():\tERROR: age sdl read failed, defaulting:')
        PtDebugPrint(('tldn3FloorElevator.OnServerInitComplete():\t%s=%d, %s=%d' % (kStringAgeSDLPwrOn,
         elevPwrOn,
         kStringAgeSDLElvLocked,
         elevLocked)))
        PtDebugPrint(('tldn3FloorElevator.OnServerInitComplete():\t%s=%d, %s=%d' % (kStringAgeSDLElvCurrFloor,
         elevCurrFloor,
         kStringAgeSDLElvIdle,
         elevIdle)))
        if (len(PtGetPlayerList()) == 0):
            PtDebugPrint('tldn3FloorElevator.OnServerInitComplete():\tsolo player... initializing elevator state')
            if (not elevIdle):

So yah, I ran away screaming in insanity.......



A few weeks ago I got brave and decided to give this another go. This time I opened up Er'cana's Bakery Elevator Platform python file. I saw that it was no where near as complex as Teledahn's and decided to edit it and give it a try:

from Plasma import *
from PlasmaTypes import *
import string
ActElevBtn = ptAttribActivator(1, 'clk: elevator button')
SDLElevPos = ptAttribString(2, 'SDL: elevator pos')
RespElevClk = ptAttribResponder(3, 'resp: elevator clicker')
SDLElevBusy = ptAttribString(4, 'SDL: elevator busy')
boolElevPos = 0
boolElevBusy = 0
AutoDown = 0
LocalAvatar = None
class neolElev(ptResponder,):
    __module__ = __name__

    def __init__(self):
        ptResponder.__init__(self)
        self.id = 101267
        self.version = 1



    def OnFirstUpdate(self):
        pass


    def OnServerInitComplete(self):
        global boolElevBusy
        global boolElevPos
        ageSDL = PtGetAgeSDL()
        ageSDL.setFlags(SDLElevPos.value, 1, 1)
        ageSDL.sendToClients(SDLElevPos.value)
        ageSDL.setFlags(SDLElevBusy.value, 1, 1)
        ageSDL.sendToClients(SDLElevBusy.value)
        ageSDL.setNotify(self.key, SDLElevPos.value, 0.0)
        ageSDL.setNotify(self.key, SDLElevBusy.value, 0.0)

It worked like a champ! My elevator went up and down............

Except there was no python for the call buttons. Er'cana's platform has a way for the player to get to it no matter where it's at.

So I took a deep breath and dove back into Teledahn's python file.

It's not really that complex. A lot of what you saw in the beginning of the file is for setting up things for Max. Here is what the begining looks like now that I've modified it:

from Plasma import *
from PlasmaTypes import *
actSendUp = ptAttribActivator(1, 'Actvtr: Elev Up Btn')
actSendDn = ptAttribActivator(2, 'Actvtr: Elev Dn Btn')
actCall1 = ptAttribActivator(3, 'Actvtr: Call Btn Top')
actCall2 = ptAttribActivator(4, 'Actvtr: Call Btn Bot')
resp1to2 = ptAttribResponder(5, 'Rspndr: 1 to 2')
resp2to1 = ptAttribResponder(6, 'Rspndr: 2 to 1')
respBuzz = ptAttribResponder(7, 'Rspnder: Buzz')
actSubworld = ptAttribActivator(8, 'Actvtr: subworld region')
elevCurrFloor = 1
elevIdle = 1
kStringAgeSDLElvCurrFloor = 'neolElevatorCurrentFloor'
kStringAgeSDLElvIdle = 'neolElevatorIdle'
AgeStartedIn = None
class neol2FloorElevator(ptResponder,):
    __module__ = __name__

    def __init__(self):
        ptResponder.__init__(self)
        self.id = 12282010
        version = 1
        self.version = version

And here is what it looks like when you select it as your python file in Max:

NeoElevcurs4.jpg

Now there were some things I had to change: Teledahn has 3 floors, Neolbah only 2. Teledahn's elevator has 2 buttons, one up and one down. Neolbah only had 1 button.

So I set about in Max to add the extra button to my elevator:

NeoElevcurs1.jpg

Now it was good to go, right?

Wrong.

The only thing my elevator would do is go down. It would not go up, nor would it come when called.

So I went back and looked at Teledahn in PrpShop:

NeoElevcurs2.jpg

First thing I noticed was that they were using xAgeSDLIntChange to change the SDL BYTE for the elevator's current floor. Okay, cool. But WHAT was telling it to do that? Ah, look up in that picture again. We see that we have a Detector (activator) called cDtctDone1to2. What is this activator?

Why it's a Animation Event detector! Here it is right here!

NeoElevcurs3.jpg

So what did that mean?

It means that I needed to set up markers on the dope sheet for the elevator's up and down animation:

NeoElevcurs7.jpg

And then set up the Animation Event detector component:

NeoElevcurs6.jpg

And last I needed to set up the xAgeSDLIntChange python file for each component that was going to need it:

NeoElevcurs5.jpg

And now yes! I have a working, multiplayer friendly elevator finally!

Yes! The "curse" had been lifted........

Or had it? The very same night I posted this on the Maintainers forum, their server crashed and the forum's data got wiped...............

que Twighlight Zone Theme

So why not have good old D'Lanor make us a Global python file for elevators?

Weeeeelllllll......I won't say it's impossible.....but it would be a LOT of work. He would have to create a python file that somehow can be used for any and all situations:

How many different floors? How many call buttons? How many different responders? Is there a power button? Is there a locking device? Oh this list could go on and on and on.

He'd have to create a MONSTER python file, and somehow think of every possible way someone could do an elevator or ride that is like an elevator.

I think that's asking a bit much of him....heheheh.


Return To: Andy's Corner