Lost with the FSM. How do you control program flow or paging

I need a meta overview of panda3d program structure.
Lets say we have 4 screens.
Sign in
Options
Play Game
Quit Game

You have Class World with all the game logic, init and everything else.
You have Class Options for setting the game options and changing them.
You have a class GameFSM that says what can go when.
You have the Sign In page class and a Quit that saves the game and all that.

Now how is it put all together?

Lets say you are in World with the game running. How do you pause all that and jump into Option State? What class inherits what? Who is king? How is it all put together? How do you pause the game mid running but then run something else? How do you pass data between the states/classes?

Hope this makes sense. I need an overview of a complete games structure and how the FSM fits in. How stuff is started and stopped.

Big question but it really has me lost.

My totally lost code!
pasteall.org/5208/python

As a side issue is line 188. I pass in “other” so that I can turn off something in the mother class. Line 277 is where I turn off the avatar in the world class from reacting to keys typed here. 614 is where it turns it off in the main world class. I ideas is easy. When playing the keys move the avatar but when chatting, your typing must not move the avatar. I really suck at OOP and needs some help with all this.

Thanks all!

I’d say it’s your personal preference of coding style. Sometimes, it’s good with FSM, sometimes it’s not.

Easy, just pause all game only tasks, (not framework tasks), game only intervals, animations, movies, sounds (except background music perhaps) :
discourse.panda3d.org/viewtopic.php?t=4439

You don’t have to do that exactly with a function call or upon construction. You could use messenger (messenger.send(‘anEvent’,[extraArgs])), so it’s event driven, and those classes instances don’t have to know each other.

I wouldn’t go that way. It’s messy.
I used to alter base.buttonThrowers[0].node()'s prefix in order to temporarily block all user input events. Say, when the user is playing, the prefix is “game-”, when user need to chat and type something that should not interfere the game events, change the prefix to “chat-” or so. During chatting period, if you need to accept some events, you prepend that prefix to the event’s name, say : accept(‘chat-event1’, doStuff). And when user gets back to the game, restore the prefix to “game-”, it’s that clean and simple.

But you still have not answered by core question. How do you do it?

Set up the keyboard on off message so that it turns on the keyboard on/off value. Thanks, good idea. It still has not answered by programming question though with normal OOP where we don’t have the tool of the message system to do it. What is the normal way in a case like this? This problem has been bugging me ever since I started the switch to OOP.

Thanks for the help!

It’s up to you which class should be the KING, it could be World, it could be GameFSM, or World which inherits from FSM.

I personally don’t use FSM, since I usually need more than 1 exit function for some states, depends on what the next state is. So, yes I do passing some important object to constructor, or simply exposing it to builtins, or the minor class simply picks it from main’s global namespace.

What’s exactly confusing you ?

Here is a good example. Class Network gets called when a message comes in from the server. It needs to add an avatar to the list but the avatar list is not seen in this context. How can the avatar list be seen by all and worked on while still being OOP style? I know I could make it a global var. So how do I get avatar, network and world all passing that data around correctly? Before I just had network code in world but that is not OOP so I cut it out and made the network class that has all the network code.

class Avatar():
    count = 0
    def __init__(self):
        self.__class__.count += 1
        print "add player count ", self.__class__.count
        # load a player
        self.bodyNode = render.attachNewNode('Body')
        self.bodyNode.setScale(.05, .05, .05)
        #self.face = loader.loadModel(modelLocation + 'Face2' + modelType) # face was the first
        #self.face.setScale(.05, .05, .05)

        self.bodyNode = Actor.Actor(modelLocation + 'Spotty_night_alienBaseWalk' + modelType,
            {'walk': modelLocation + 'Spotty_night_alienBaseWalk-walk' + modelType,
            'stand': modelLocation + 'Spotty_night_alienBaseWalk-Stand' + modelType})
        self.bodyNode.setScale(0.1, 0.1, 0.1)
        self.bodyNode.setTransparency(False)


        #self.face.flattenStrong()
        self.face = render.attachNewNode('Face')
        self.face.reparentTo(self.bodyNode)

        self.bodyNode.reparentTo(render)

        self.smoothMover = SmoothMover() # Just a test of making this work for the face.
        self.smoothMover.setPredictionMode(False)
        self.smoothMover.setSmoothMode(True)

        self.name = ''
        self.nick = ''
        self.IP = ''
        self.StartPlacePosHpr = ((1024, 1024, 0), (0, 0, 0))
        self.Cash = 0
        self.Race = 'face'
        self.Skin = 0
        self.avatarFSM = AvatarFSM()

        self.avatarFSM.request('Walk',1)
        self.avatarFSM.request('Stand')

    def walk(self):
        self.bodyNode.loop('walk')

    def __del__(self):
        print "remove ", self.__class__.count
        self.face.cleanup()
        self.bodyNode.cleanup()
        self.face.removeNode()
        self.bodyNode.removeNode()

class Network(DirectObject.DirectObject, Avatar): # we only need one instance of this!!
    client = PandaNetworkingClient('journeytothestars.webhop.net', 9095)
    while not client.startNetwork() : print 'init failed connect, retrying'
    playerDict = {}
    playerCount = 0

    #messenger.toggleVerbose()
    #print messenger
    print "hi one"
    def __init__(self):
        self.playerDict = {}
        # need to check for errors here, I think
        # SET UP broadcasting of data at set times.

    # START PLAYER
    def startPlayersIn(self, dataIn):
            for c in dataIn['packets'] :
                print 'id, count, playerDict', self.client.ID, c, self.playerDict
                if c != self.client.ID and c not in self.playerDict:
                    Network.playerCount += 1 #
                    self.playerDict[c] = Network.playerCount
                    # add a player record
                    self.avatar.append(Avatar())
                    print Network.playerCount, self.client.ID
                    self.avatar[Network.playerCount].face.setPos(1024, 1014,
                        self.terrain.getElevation(1024, 1014) * 
                             self.ground.getSz()+.1)
                    self.avatar[Network.playerCount].face.setHpr(0.0, 0.0, 0.0)
                    self.avatar[Network.playerCount].face.reparentTo(render)

class World(DirectObject.DirectObject, Network):
    def __init__(self, playerNick):
        self.smooth = True
        # messenger.toggleVerbose()
        # print messenger
        self.programFSM = ProgramFSM()
        self.programFSM.request('LogIn')
        #self.programFSM.request('StartNetwork', self.client)
        self.programFSM.request('Game')
        # NETWORK INIT

        self.id = 0
        self.playerNick = playerNick
        self.avatar = []
        self.avatar.append(Avatar())
        self.avatar[0].bodyNode.hide() # no need to see own avatar in first person mode

So, you don’t want to use global var, builtins, and messaging system. Is that right ?
I can only think of moving World instance’s “avatars” attribute to be World class’ attribute.

Why don’t you want to use messaging system anyway ?
It’s a simple class to glue things together, cleanly. You can always create your own simpler one if you like. What’s stopping you ?

Good code should have a clean in and a clean out path for all data. Debugging becomes hard when data goes to the mist and comes from the mist. Besides I can’t be the first programmer to have this problem. There must be a standard way of doing it.