Moving game logic from alcscript to Python

Help bring our custom Ages to life! Share tips and tricks, as well as code samples with other developers.

Moving game logic from alcscript to Python

Postby ddb174 » Thu May 21, 2009 1:11 pm

Moving game logic from alcscript to Python

Basically, it's much easier to debug, modify, and make complex Python scripts, so when possible, it may be better to do as much in Python as possible.

To that end, here are some (no doubt imperfect) things Wodan and I used in the present working version of Oolbahnneea. (It originated as a modified Boblishman script, so some of the names reflect that.)

The Alcscipt for two (for brevity) of the twelve buttons and the door that is to be opened.
Code: Select all
SWDoorButDec:
    animations:
        - name: SWdoorbutdec
          autostart: 0
          loop: 0


Button1Oneshot:
   logic:
       actions:
          - type: oneshot
            name: Button1Oneshot
            oneshot:
                animation: DoorButtonTouch   

Button1:
    animations:
        - name: button1click
          autostart: 0
          loop: 0
    logic:
        modifiers:
          - tag: AutoClick
            cursor: poised
            flags:
              - localelement
            activators:
              - type: objectinvolume
                remote: Button1swing
                triggers:
                  - any
            conditions:
              - type: activator
                activators:
                  - type: picking
              - type: objectinbox
                satisfied: true
            actions:
              - type: pythonfile
                ref: $BoolToggle
              - type: responder
                ref: $But1resp
        actions:
          - type: pythonfile
            tag: BoolToggle
            pythonfile:
                file: oolbPuzz2Click
                parameters:
                  - type: activator
                    ref: logicmod:$AutoClick
                  - type: string   
                    value: puzzle2button1
          - type: pythonfile
            tag: BoolRespond
            pythonfile:
                file: xAgeSDLBoolRespond
                parameters:
                  - type: string
                    value: VerandaTuerLinks
                  - type: responder
                    ref: :Door_2Open
                  - type: responder
                    ref: :Door_2Close
                  - type: bool
                    value: false
                  - type: bool
                    value: true
          - type: responder
            tag: But1resp
            responder:
                states:
                  - cmds:
                      - type: oneshotmsg
                        params:
                            receivers:
                              - oneshotmod:Button1Oneshot
                            #callbacks:
                            #  - marker: "DoorButtonTouch"
                            #    receiver: respondermod:Door_2Open
                            #    user: 0
                        waiton: -1
                    nextstate: 1
                    waittocmd: 0
                curstate: 0
                flags:
                  - detecttrigger



Button2Oneshot:
   logic:
       actions:
          - type: oneshot
            name: Button2Oneshot
            oneshot:
                animation: DoorButtonTouch

Button2:
    animations:
        - name: button2click
          autostart: 0
          loop: 0
    logic:
        modifiers:
          - tag: AutoClick
            cursor: poised
            flags:
              - localelement
            activators:
              - type: objectinvolume
                remote: Button2swing
                triggers:
                  - any
            conditions:
              - type: activator
                activators:
                  - type: picking
              - type: objectinbox
                satisfied: true
            actions:
              - type: pythonfile
                ref: $BoolToggle
              - type: responder
                ref: $But2resp
        actions:
          - type: pythonfile
            tag: BoolToggle
            pythonfile:
                file: oolbPuzz2Click
                parameters:
                  - type: activator
                    ref: logicmod:$AutoClick
                  - type: string   
                    value: puzzle2button2
          - type: responder
            tag: But2resp
            responder:
                states:
                  - cmds:
                      - type: oneshotmsg
                        params:
                            receivers:
                              - oneshotmod:Button2Oneshot
                            #callbacks:
                            #  - marker: "DoorButtonTouch"
                            #    receiver: respondermod:Door_2Open
                            #    user: 0
                        waiton: -1
                    nextstate: 1
                    waittocmd: 0
                curstate: 0
                flags:
                  - detecttrigger


Door_2:
    animations:
        - name: Door_2Open1
          autostart: 0
          loop: 0
    logic:
        actions:
          - type: responder
            name: Door_2Open
            responder:
               states:
                - cmds:
                   #- type: oneshotmsg
                   #  params:
                   #     receivers:
                   #      - oneshotmod:Button1Oneshot
                   #     callbacks:
                   #      - marker: "DoorButtonTouch"
                   #        receiver: respondermod:Door_2Open
                   #        user: 0
                   #  waiton: -1
                   - type: animcmdmsg
                     params:
                        receivers:
                         - 006D:Door_2
                        animname: Door_2Open1
                        cmds:
                         - setforewards
                         - continue
                     waiton: -1
                   #- type: animcmdmsg
                   #  params:
                   #     receivers:
                   #      - 006D:Button1
                   #     animname: button1click
                   #     cmds:
                   #      - setforewards
                   #      - continue
                   #  waiton: 0
                   - type: animcmdmsg
                     params:
                        receivers:
                         - 006D:SWDoorButDec
                        animname: SWdoorbutdec
                        cmds:
                         - setforewards
                         - continue
                     waiton: -1
                  nextstate: 1
                  waittocmd: 0
               curstate: 0
               flags:
                 - detecttrigger
          - type: responder
            name: Door_2Close
            responder:
               states:
                - cmds:
                   #- type: oneshotmsg
                   #  params:
                   #     receivers:
                   #      - oneshotmod:Button1Oneshot
                   #     callbacks:
                   #      - marker: "DoorButtonTouch"
                   #        receiver: respondermod:Door_2Close
                   #        user: 0
                   #  waiton: -1
                   - type: animcmdmsg
                     params:
                        receivers:
                         - 006D:Door_2
                        animname: Door_2Open1
                        cmds:
                         - setbackwards
                         - continue
                     waiton: -1
                   #- type: animcmdmsg
                   #  params:
                   #     receivers:
                   #      - 006D:Button1
                   #     animname: button1click
                   #     cmds:
                   #      - setforewards
                   #      - continue
                   #  waiton: 0
                   - type: animcmdmsg
                     params:
                        receivers:
                         - 006D:SWDoorButDec
                        animname: SWdoorbutdec
                        cmds:
                         - setforewards
                         - continue
                     waiton: -1
                  nextstate: 0
                  waittocmd: 0
               curstate: 0
               flags:
                 - detecttrigger



The Python file that does all the logic:
Code: Select all
# emacs-mode: -*- python-*-
from Plasma import *
from PlasmaTypes import *
import string
actTrigger = ptAttribActivator(1, 'Activator')
buttonName = ptAttribString(2,'Button Name')
extraArgument = ptAttribString(3,'Extra Argument')
#actTrigger = ptAttribActivator(1, 'Activator')
#stringVarName = ptAttribString(2, 'Age SDL Var Name')
#stringInfo = ptAttribString(5, 'Extra info to pass along')
#boolCurrentValue = false
AgeStartedIn = None
class oolbPuzz2Click(ptResponder,):
    __module__ = __name__

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



    def OnFirstUpdate(self):
        print 'oolb.onfirstupdate'
        global AgeStartedIn
        AgeStartedIn = PtGetAgeName()
        if (not ((type(buttonName.value) == type('')) and (buttonName.value != ''))):
            PtDebugPrint('ERROR: oolbPuzz2Click.OnFirstUpdate():\tERROR: missing SDL var name')



    def OnServerInitComplete(self):
        print 'oolb.onserverinitcomplete'
        global boolCurrentValue
        if (AgeStartedIn == PtGetAgeName()):
            ageSDL = PtGetAgeSDL()
            if ((type(buttonName.value) == type('')) and (buttonName.value != '')):
                sdlname = 'VerandaTuerLinks'
                ageSDL.setNotify(self.key, sdlname, 0.0)
                ageSDL.setFlags(sdlname, 1, 1)
                ageSDL.sendToClients(sdlname)
                #try:
                #    boolCurrentValue = ageSDL[buttonName.value][0]
                #except:
                #    PtDebugPrint('ERROR: xAgeSDLBoolToggle.OnServerInitComplete():\tERROR reading age SDL')
                #PtDebugPrint(('DEBUG: xAgeSDLBoolToggle.OnServerInitComplete():\t%s = %d' % (stringVarName.value,
                # boolCurrentValue)))
            else:
                PtDebugPrint('ERROR: oolbPuzz2Click.OnServerInitComplete():\tERROR: missing SDL var name')



    def OnNotify(self, state, id, events):
        #global boolCurrentValue
        print 'oolbPuzz2Click.OnNotify'
        #print 'state: '+`state`
        #print 'id: '+`id`
        #print 'actTrigger.id: '+`actTrigger.id`
        #if not state:
        #    print 'not state'
        #    return
        if ((not state) or (id != actTrigger.id)):
        #if ((not state)):
            print '  ignoring because of state or actTrigger'
            return
        #print 'skipping PtWasLocallyNotified for some reason.'
        if (not PtWasLocallyNotified(self.key)):
            return
        #print '2'
        if ((type(actTrigger.value) == type([])) and (len(actTrigger.value) > 0)):
            PtDebugPrint(('  DEBUG: oolbPuzz2Click.OnNotify():\t local player requesting %s change via %s' % (buttonName.value,
             actTrigger.value[0].getName())))
        if ((type(buttonName.value) != type('')) or (buttonName.value == '')):
            PtDebugPrint('  ERROR: oolbPuzz2Click.OnNotify():\tERROR: missing SDL var name')
            return
        #print '3'

        if (AgeStartedIn == PtGetAgeName()):
            #solution: 12, 10, 8, 7.
            ageSDL = PtGetAgeSDL()
            button = buttonName.value
            print '  button clicked!: '+`button`
           
            if button.startsWith('puzzle2'):
                count = ageSDL['Puzzle2CorrectCount'][0]
                print '  count: '+`count`
                #if ((button=='puzzle2button12') and (count==0)):
                if ((button=='puzzle2button12')):
                    print '  first button...'
                    ageSDL['Puzzle2CorrectCount'] = (1,)
                    ageSDL['VerandaTuerLinks'] = (0,)
                elif ((button=='puzzle2button10') and (count==1)):
                    print '  second button...'
                    ageSDL['Puzzle2CorrectCount'] = (2,)
                    ageSDL['VerandaTuerLinks'] = (0,)
                elif ((button=='puzzle2button8') and (count==2)):
                    print '  third button...'
                    ageSDL['Puzzle2CorrectCount'] = (3,)
                    ageSDL['VerandaTuerLinks'] = (0,)
                elif ((button=='puzzle2button7') and (count==3)):
                    print '  forth button...'
                    PtAtTimeCallback(self.key, 3.0, 1)
                    #ageSDL['Puzzle2CorrectCount'] = (4,)
                    ageSDL['Puzzle2CorrectCount'] = (0,)
                    #ageSDL['VerandaTuerLinks'] = (1,)
                else:
                    print '  wrong button...'
                    ageSDL['Puzzle2CorrectCount'] = (0,)
                    ageSDL['VerandaTuerLinks'] = (0,)
            elif button.startsWith('puzzle3'):
                print 'Puzzle3 button clicked!'                           
           
            print '  done!'

    def OnTimer(self, id):
        if(id==1):
            print 'Opening door...'
            ageSDL = PtGetAgeSDL()
            ageSDL['VerandaTuerLinks'] = (1,)

    def OnSDLNotify(self, VARname, SDLname, playerID, tag):
        #global boolCurrentValue
        print 'oolbPuzz2Click.OnSDLNotify'
        if (AgeStartedIn == PtGetAgeName()):
            ageSDL = PtGetAgeSDL()
            #if (VARname == stringVarName.value):
            #    PtDebugPrint(('DEBUG: xAgeSDLBoolToggle.OnSDLNotify():\t VARname:%s, SDLname:%s, tag:%s, value:%d' % (VARname,
            #     SDLname,
            #     tag,
            #     ageSDL[stringVarName.value][0])))
            #    boolCurrentValue = ageSDL[stringVarName.value][0]



glue_cl = None
glue_inst = None
glue_params = None
glue_paramKeys = None
try:
    x = glue_verbose
except NameError:
    glue_verbose = 0

def glue_getClass():
    global glue_cl
    if (glue_cl == None):
        try:
            cl = eval(glue_name)
            if issubclass(cl, ptModifier):
                glue_cl = cl
            elif glue_verbose:
                print ('Class %s is not derived from modifier' % cl.__name__)
        except NameError:
            if glue_verbose:
                try:
                    print ('Could not find class %s' % glue_name)
                except NameError:
                    print 'Filename/classname not set!'
    return glue_cl



def glue_getInst():
    global glue_inst
    if (type(glue_inst) == type(None)):
        cl = glue_getClass()
        if (cl != None):
            glue_inst = cl()
    return glue_inst



def glue_delInst():
    global glue_inst
    global glue_cl
    global glue_paramKeys
    global glue_params
    if (type(glue_inst) != type(None)):
        del glue_inst
    glue_cl = None
    glue_params = None
    glue_paramKeys = None



def glue_getVersion():
    inst = glue_getInst()
    ver = inst.version
    glue_delInst()
    return ver



def glue_findAndAddAttribs(obj, glue_params):
    if isinstance(obj, ptAttribute):
        if glue_params.has_key(obj.id):
            if glue_verbose:
                print 'WARNING: Duplicate attribute ids!'
                print ('%s has id %d which is already defined in %s' % (obj.name,
                 obj.id,
                 glue_params[obj.id].name))
        else:
            glue_params[obj.id] = obj
    elif (type(obj) == type([])):
        for o in obj:
            glue_findAndAddAttribs(o, glue_params)

    elif (type(obj) == type({})):
        for o in obj.values():
            glue_findAndAddAttribs(o, glue_params)

    elif (type(obj) == type(())):
        for o in obj:
            glue_findAndAddAttribs(o, glue_params)




def glue_getParamDict():
    global glue_paramKeys
    global glue_params
    if (type(glue_params) == type(None)):
        glue_params = {}
        gd = globals()
        for obj in gd.values():
            glue_findAndAddAttribs(obj, glue_params)

        glue_paramKeys = glue_params.keys()
        glue_paramKeys.sort()
        glue_paramKeys.reverse()
    return glue_params



def glue_getClassName():
    cl = glue_getClass()
    if (cl != None):
        return cl.__name__
    if glue_verbose:
        print ('Class not found in %s.py' % glue_name)
    return None



def glue_getBlockID():
    inst = glue_getInst()
    if (inst != None):
        return inst.id
    if glue_verbose:
        print ('Instance could not be created in %s.py' % glue_name)
    return None



def glue_getNumParams():
    pd = glue_getParamDict()
    if (pd != None):
        return len(pd)
    if glue_verbose:
        print ('No attributes found in %s.py' % glue_name)
    return 0



def glue_getParam(number):
    pd = glue_getParamDict()
    if (pd != None):
        if (type(glue_paramKeys) == type([])):
            if ((number >= 0) and (number < len(glue_paramKeys))):
                return pd[glue_paramKeys[number]].getdef()
            else:
                print ('glue_getParam: Error! %d out of range of attribute list' % number)
        else:
            pl = pd.values()
            if ((number >= 0) and (number < len(pl))):
                return pl[number].getdef()
            elif glue_verbose:
                print ('glue_getParam: Error! %d out of range of attribute list' % number)
    if glue_verbose:
        print 'GLUE: Attribute list error'
    return None



def glue_setParam(id, value):
    pd = glue_getParamDict()
    if (pd != None):
        if pd.has_key(id):
            try:
                pd[id].__setvalue__(value)
            except AttributeError:
                if isinstance(pd[id], ptAttributeList):
                    try:
                        if (type(pd[id].value) != type([])):
                            pd[id].value = []
                    except AttributeError:
                        pd[id].value = []
                    pd[id].value.append(value)
                else:
                    pd[id].value = value
        elif glue_verbose:
            print "setParam: can't find id=",
            print id
    else:
        print 'setParma: Something terribly has gone wrong. Head for the cover.'



def glue_isNamedAttribute(id):
    pd = glue_getParamDict()
    if (pd != None):
        try:
            if isinstance(pd[id], ptAttribNamedActivator):
                return 1
            if isinstance(pd[id], ptAttribNamedResponder):
                return 2
        except KeyError:
            if glue_verbose:
                print ('Could not find id=%d attribute' % id)
    return 0



def glue_isMultiModifier():
    inst = glue_getInst()
    if isinstance(inst, ptMultiModifier):
        return 1
    return 0



def glue_getVisInfo(number):
    pd = glue_getParamDict()
    if (pd != None):
        if (type(glue_paramKeys) == type([])):
            if ((number >= 0) and (number < len(glue_paramKeys))):
                return pd[glue_paramKeys[number]].getVisInfo()
            else:
                print ('glue_getVisInfo: Error! %d out of range of attribute list' % number)
        else:
            pl = pd.values()
            if ((number >= 0) and (number < len(pl))):
                return pl[number].getVisInfo()
            elif glue_verbose:
                print ('glue_getVisInfo: Error! %d out of range of attribute list' % number)
    if glue_verbose:
        print 'GLUE: Attribute list error'
    return None



# local variables:
# tab-width: 4


Now then, Button1Oneshot is the avatar's hand animation, and Button1 is the clickable itself. Note that here we simultaneously call the BoolToogle and But1resp actions when it is clicked. The But1resp action is the avatar animation, and the BoolToggle calls our Python file. (BoolToggle is only called that for historic reasons.) We also have the (uncalled) BoolRespond action in the first button (it doesn't actually matter what object we stick it on, since it is only called at initialisation.) This ties the door's animation to sdl in the usual way, which may be useful because we can then control the door animation in Python via SDL and have it network-synched.

As for the Python code: firstly, ignore the OnServerInitComplete and OnSDLNotify functions. OnNotify is what is called when a button is clicked. The starting stuff checks the state (to make sure the button was going down, not up). In this case, the logic really begins with the "if button.startsWith('puzzle2'):" line. And from here, it is easy to add/modify logic since it is in pure Python!

I hope this may help someone is some way, and feel free to ask questions or recommend simplifications!
ddb174
 
Posts: 928
Joined: Thu Apr 10, 2008 7:28 pm

Re: Moving game logic from alcscript to Python

Postby D'Lanor » Thu May 21, 2009 4:18 pm

There is another important argument to use Python. It is needed to handle multiplayer issues. The Python functions sceneobject.isLocallyOwned(), PtWasLocallyNotified() and netForce/netPropagate control are simply indispensable in the online environment. We are going to need those for OSMO.

The problem is that most writers know how to use AlcScript but only a few can cope with Python.
"It is in self-limitation that a master first shows himself." - Goethe
User avatar
D'Lanor
 
Posts: 1980
Joined: Sat Sep 29, 2007 4:24 am

Re: Moving game logic from alcscript to Python

Postby Jojon » Thu May 21, 2009 11:14 pm

Here there seems to be room for a plain english tutorial, easing non CS graduates into relevant plasma/python/API/networking concepts, before even showing a single bit of script... :9

I guess Adam's Plasma 101 would have encompassed that, had he had time to complete it, but it's stil a bit "too technical" - well, it outright says that it assumes the reader has some relevant education.
Jojon
 
Posts: 1116
Joined: Sun Sep 30, 2007 5:49 am


Return to Scripting

Who is online

Users browsing this forum: No registered users and 0 guests

cron