""" *==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

Additional permissions under GNU GPL version 3 section 7

If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

 *==LICENSE==* """
"""
This module is contains the datatypes and constants for
interfacing with the Plasma 2.0 engine.
"""
from Plasma import *
from PlasmaConstants import *

####################################
# Utility functions
####################################
false=0
true=1

# OnNotify Event enums
kCollisionEvent=PtEventType.kCollision       # [1]=enter flag, [2]=hitter(probably avatar), [3]=hittee
kPickedEvent=PtEventType.kPicked             # [1]=enter flag, [2]=picker(probably avatar), [3]=pickee, [4]=hitpoint(world) [5]=hitpoint(local)
kControlKeyEvent=PtEventType.kControlKey     # [1]=key id, [2]=down flag
kVariableEvent=PtEventType.kVariable         # [1]=name, [2]=data type, [3]=data
kFacingEvent=PtEventType.kFacing             # [1]=enabled flag, [2]=facer(probably avatar), [3]=facee, [4]=dot product
kContainedEvent=PtEventType.kContained       # [1]=entering flag, [2]=contained(probably avatar), [3]=container
kActivateEvent=PtEventType.kActivate         # [1]=active flag, [2]=activate flag
kCallbackEvent=PtEventType.kCallback         # [1]=callback id
kResponderStateEvent=PtEventType.kResponderState  # [1]=state id
kMultiStageEvent=PtEventType.kMultiStage     # [1]=what stage, [2]=event(see below), [3]=avatar
kSpawnedEvent=PtEventType.kSpawned           # [1]=spawner, [2]=spawnee (usually avatar)
kClickDragEvent=PtEventType.kClickDrag       # not used yet
kOfferLinkingBook=PtEventType.kOfferLinkingBook	# [1]=offerer, [2]=link panel ID of age offered

    
# OnNotify Var Event Data Types
kVarNumberType=PtNotifyDataType.kFloat
kVarKeyType=PtNotifyDataType.kKey
# OnNotify MultiStageEvent - what event types
kEnterStage=PtMultiStageEventType.kEnterStage
kBeginingOfLoop=PtMultiStageEventType.kBeginingOfLoop
kAdvanceNextStage=PtMultiStageEventType.kAdvanceNextStage
kRegressPrevStage=PtMultiStageEventType.kRegressPrevStage
# Behavior - gotoStage
kStageExitBrain=-1      # sending ptAttribBehavior to stage -1 will exit brain

# OnGUINotify Control Types
kDialog=1
kButton=2
kDraggable=3
kListBox=4
kTextBox=5
kEditBox=6
kUpDownPair=7
kKnob=8
kDragBar=9
kCheckBox=10
kRadioGroup=11
kDynamicTextControl=12
kMultiLineEdit=13
# GUIControlListBox String Justify Types
kLeftJustify=1
kRightJustify=2
# GUIControlListBox String inherit flag
kNoInherit=0
kInheritFromNormal=1
kInheritFromSelect=2
kSelectDetermined=3
kSelectUseGUIColor=4
# GUIControlMultiLineEdit style flags (additive)
kFontBold=1
kFontItalic=2
kFontShadowed=4

# OnGUINotify Event Types
kShowHide=1             # show or hide change (only on kDialog)
kAction=2               # kButton clicked, kListBox item clicked on, kEditBox hit enter
kValueChanged=3         # value changed in control (could be from kUpDownPair,kKnob,kCheckBox,kRadioGroup
kDialogLoaded=4         # the dialog has just been loaded
kFocusChange=5          # the focus changed from one control to another, or none, within the dialog
kExitMode = 6		# Modal dialog received an exit mode command
kInterestingEvent = 7   # an interesting event happened

# OnRoomLoad 'what' types
kLoaded=1
kUnloaded=2

# Clothing group Types and clothing types
kMaleClothingGroup=0
kFemaleClothingGroup=1
kAnyClothingItem=-1
kPantsClothingItem=0
kShirtClothingItem=1
kLeftHandClothingItem=2
kRightHandClothingItem=3
kFaceClothingItem=4
kHairClothingItem=5
kLeftFootClothingItem=6
kRightFootClothingItem=7
kAccessoryClothingItem=8

#Debug print levels
kDebugDumpLevel = 1
kWarningLevel = 2
kErrorLevel = 3
kAssertLevel = 4


def PtAssert(cond, msg):
    "Plasma assert. Just like the Python one but we can set it to NOP in release"
    assert cond,msg

def PtDebugPrint(*msgs,**keywords):
    "Plasma debug print. Will be NOP'd when released"
    try:
        level = keywords['level']
    except LookupError:
        level = kErrorLevel
    if level >= PtGetPythonLoggingLevel():
        if level == 4:
            PtAssert(0,msgs[0])
        else:
            for msg in msgs:
                print msg

def PtGetObjectName(obj):
    "Given a ptSceneobject, return its name"
    if isinstance(obj,ptSceneobject):
        if type(obj.getKey()) != type(None):
            return obj.getKey().getName()
    return "nil"

# add an event that is in the form of a list
# ...to a notify message
def PtAddEvent(notify,event):
    "Add an event of any type to a ptNotify message object"
    if type(event) != type([]):
        print "Unrecognized event record structure"
        return
    if event[0] == kCollisionEvent:
        notify.addCollisionEvent(event[1],event[2].getKey(),event[3].getKey())
    elif event[0] == kPickedEvent:
        notify.addPickEvent(event[1],event[2].getKey(),event[3].getKey(),event[4])
    elif event[0] == kControlKeyEvent:
        notify.addControlKeyEvent(event[1],event[2])
    elif event[0] == kVariableEvent:
        if event[2] == PtNotifyDataType.kFloat:
            notify.addVarFloat(event[1],event[3])
        elif event[2] == PtNotifyDataType.kInt:
            notify.addVarInt(event[1],event[3])
        elif event[2] == PtNotifyDataType.kNull:
            notify.addVarNull(event[1])
        elif event[2] == kVarKeyType:
            notify.addVarKey(event[1],event[3])
    elif event[0] == kFacingEvent:
        notify.addFacingEvent(event[1],event[2].getKey(),event[3].getKey(),event[4])
    elif event[0] == kContainedEvent:
        notify.addContainerEvent(event[1],event[2].getKey(),event[3].getKey())
    elif event[0] == kActivateEvent:   
        notify.addActivateEvent(event[1],event[2])
    elif event[0] == kCallbackEvent:   
        notify.addCallbackEvent(event[1])
    elif event[0] == kResponderStateEvent:
        notify.addResponderState(event[1])
    else:
        print "Unrecognized event type %d" % (event[0])
        
# add a list of events into a ptNotify message
def PtAddEvents(notify, events):
    "Add a list of events to a ptNotify message object"
    if type(events) == type([]):
        for event in events:
            PtAddEvent(notify,event)

# find the avatar in event record list
def PtFindAvatar(events):
    "Find the avatar in one of the event records"
    for event in events:
        if event[0]==kCollisionEvent or event[0]==kPickedEvent or event[0]==kFacingEvent or event[0]==kContainedEvent or event[0]==kSpawnedEvent:
            return event[2]
        if event[0] == kMultiStageEvent:
            return event[3]
    # didn't find one
    return None
    
####################################
# Exceptions
####################################
import exceptions
class ptResponderStateError(exceptions.Exception):
    def __init__(self,args=None):
        self.args = args

#
# Attributes that will be exposed in Max to be filled in by <someone>

####################################
# base class for all attributes
####################################
# (This makes it easy to find all the attributes in a module)
class ptAttribute:
    def __init__(self,id,name, vislistid = -1, visliststates = []):
        self.id = id
        self.name = name
        self.vis_id = vislistid
        self.vis_states = visliststates

    def setVisInfo(self, id, stateslist):
        self.vis_id = id
        self.vis_states = stateslist

    def getVisInfo(self):
        return (self.vis_id, self.vis_states)

# base class for all list attributes
# (This makes it easy to find all the attributes that are a list)
class ptAttributeList(ptAttribute):
    def __init__(self,id,name):
        ptAttribute.__init__(self,id,name)

# Boolean attribute (Checkbox)
class ptAttribBoolean(ptAttribute):
    def __init__(self,id,name=None, default=0):
        ptAttribute.__init__(self,id,name)
        self.value = default
    def getdef(self):
        return (self.id,self.name,1,self.value)

# Integer attribute (Spinner)
class ptAttribInt(ptAttribute):
    def __init__(self,id,name=None,default=0,rang=None):
        ptAttribute.__init__(self,id,name)
        self.value = default
        self.rang = rang
    def getdef(self):
        return (self.id,self.name,2,self.value,self.rang)

# Floating point number attribute (Spinner)
class ptAttribFloat(ptAttribute):
    def __init__(self,id,name=None,default=0.0,rang=None):
        ptAttribute.__init__(self,id,name)
        self.value = default
        self.rang = rang
    def getdef(self):
        return (self.id,self.name,3,self.value,self.rang)

# String attribute (Edit box)
class ptAttribString(ptAttribute):
    def __init__(self,id,name=None,default=""):
        ptAttribute.__init__(self,id,name)
        self.value = default
    def getdef(self):
        return (self.id,self.name,4,self.value)

# Drop-down list attribute (Drop-down list combo box)
class ptAttribDropDownList(ptAttribute):
    def __init__(self,id,name=None,options=None):
        ptAttribute.__init__(self,id,name)
        self.options = options
    def getdef(self):
        return (self.id,self.name,20,self.options)

# Sceneobject attribute (pick single sceneobject box)
class ptAttribSceneobject(ptAttribute):
    def __init__(self,id,name=None,netForce=0):
        ptAttribute.__init__(self,id,name)
        self.value = None
        self.sceneobject = None
        self.netForce = netForce
    def getdef(self):
        return (self.id,self.name,5)
    def __setvalue__(self,value):
        if self.netForce:
            value.netForce(1)
        self.value = value
        self.sceneobject = self.value

# Sceneobject list attribute (pick multiple sceneobjects box)
class ptAttribSceneobjectList(ptAttributeList):
    def __init__(self,id,name=None,byObject=0,netForce=0):
        ptAttributeList.__init__(self,id,name)
        self.value = []     # start as an empty list
        self.sceneobject = self.value
        self.netForce = netForce
        if byObject:
            self.byObject = {}
        else:
            self.byObject = None
    def getdef(self):
        return (self.id,self.name,6)
    def __setvalue__(self,value):
        if self.netForce:
            value.netForce(1)
        self.value.append(value)
        if type(self.byObject) == type({}):
            name = value.getName()
            self.byObject[name] = value

# attribute list of keys
class ptAttributeKeyList(ptAttributeList):
    def __init__(self,id,name=None,byObject=0,netForce=0):
        ptAttributeList.__init__(self,id,name)
        self.value = []
        self.netForce = netForce
        if byObject:
            self.byObject = {}
        else:
            self.byObject = None
    def enable(self,objectName=None):
        if self.value != None:
            if type(objectName) != type(None) and type(self.byObject) != type(None):
                pkey = self.byObject[objectName]
                if self.netForce:
                    pkey.netForce(1)
                pkey.enable()
            elif type(self.value)==type([]):
                for pkey in self.value:
                    if self.netForce:
                        pkey.netForce(1)
                    pkey.enable()
            else:
                if self.netForce:
                    self.value.netForce(1)
                self.value.enable()
    def disable(self,objectName=None):
        if self.value != None:
            if type(objectName) != type(None) and type(self.byObject) != type(None):
                pkey = self.byObject[objectName]
                if self.netForce:
                    pkey.netForce(1)
                pkey.disable()
            elif type(self.value)==type([]):
                for pkey in self.value:
                    if self.netForce:
                        pkey.netForce(1)
                    pkey.disable()
            else:
                if self.netForce:
                    self.value.netForce(1)
                self.value.disable()
    def __setvalue__(self,value):
        if self.netForce:
            value.netForce(1)
        self.value.append(value)
        if type(self.byObject) == type({}):
            name = value.getName()
            self.byObject[name] = value

# Activator attribute (pick activator types box)
class ptAttribActivator(ptAttributeKeyList):
    def getdef(self):
        return (self.id,self.name,8)
    def enableActivator(self):
        for key in self.value:
            key.getSceneObject().physics.enable()
    def disableActivator(self):
        for key in self.value:
            key.getSceneObject().physics.disable()
    def volumeSensorIgnoreExtraEnters(self,state):
        for key in self.value:
            key.getSceneObject().volumeSensorIgnoreExtraEnters(state)

# Activator attribute (pick activator types box)
class ptAttribActivatorList(ptAttributeKeyList):
    def getdef(self):
        return (self.id,self.name,7)

# Responder attribute (pick responder types box)
class ptAttribResponder(ptAttributeKeyList):
    def __init__(self,id,name=None,statelist=None,byObject=0,netForce=0):
        ptAttributeKeyList.__init__(self,id,name,byObject,netForce)
        self.state_list = statelist
    def getdef(self):
        return (self.id,self.name,9)
    def run(self,key,state=None,events=None,avatar=None,objectName=None,netForce=0,netPropagate=1,fastforward=0):
        # has the value been set?
        if type(self.value) != type(None):
            nt = ptNotify(key)
            nt.clearReceivers()
            # see if the value is a list or byObject or a single
            if type(objectName) != type(None) and type(self.byObject) != type(None):
                nt.addReceiver(self.byObject[objectName])
            elif type(self.value)==type([]):
                for resp in self.value:
                    nt.addReceiver(resp)
            else:
                nt.addReceiver(self.value)
            if not netPropagate:
                nt.netPropagate(0)
                # ptNotify defaults to netPropagate=1
            if netForce or self.netForce:
                nt.netForce(1)
            # see if the state is specified
            if type(state) == type(0):
                raise ptResponderStateError,"Specifying state as a number is no longer supported"
            elif type(state) == type(''):
                if type(self.state_list) != type(None):
                    try:
                        idx = self.state_list.index(state)
                        nt.addResponderState(idx)
                    except ValueError:
                        raise ptResponderStateError, "There is no state called '%s'"%(state)
                else:
                    raise ptResponderStateError,"There is no state list provided"
            # see if there are events to pass on
            if type(events) != type(None):
                PtAddEvents(nt,events)
            if type(avatar) != type(None):
                nt.addCollisionEvent(1,avatar.getKey(),avatar.getKey())
            if fastforward:
                nt.setType(PtNotificationType.kResponderFF)
                # if fast forwarding, then only do it on the local client
                nt.netPropagate(0)
                nt.netForce(0)
            nt.setActivate(1.0)
            nt.send()
    def setState(self,key,state,objectName=None,netForce=0,netPropagate=1):
        # has the value been set?
        if type(self.value) != type(None):
            nt = ptNotify(key)
            nt.clearReceivers()
            # see if the value is a list or byObject or a single
            if type(objectName) != type(None) and type(self.byObject) != type(None):
                nt.addReceiver(self.byObject[objectName])
            elif type(self.value)==type([]):
                for resp in self.value:
                    nt.addReceiver(resp)
            else:
                nt.addReceiver(self.value)
            if not netPropagate:
                nt.netPropagate(0)
                # ptNotify defaults to netPropagate=1
            if netForce or self.netForce:
                nt.netForce(1)
            # see if the state is specified
            if type(state) == type(0):
                raise ptResponderStateError,"Specifying state as a number is no longer supported"
            elif type(state) == type(''):
                if type(self.state_list) != type(None):
                    try:
                        idx = self.state_list.index(state)
                        nt.addResponderState(idx)
                    except ValueError:
                        raise ptResponderStateError, "There is no state called '%s'"%(state)
                else:
                    raise ptResponderStateError,"There is no state list provided"
            # see if there are events to pass on
            nt.setType(PtNotificationType.kResponderChangeState)
            nt.setActivate(1.0)
            nt.send()

    def getState(self):
        if (type(self.value) != type(None)):
            if type(self.value)==type([]):
                for resp in self.value:
                    obj = resp.getSceneObject()
                    idx = obj.getResponderState()
                    curState = self.state_list[idx]
                    return curState
            else:
                obj = self.value.getSceneObject()
                idx = obj.getResponderState()
                curState = self.state_list[idx]
                return curState


# Responder attribute List
class ptAttribResponderList(ptAttribResponder):
    def getdef(self):
        return (self.id,self.name,10)
 
# Activator attribute (pick activator types box)
class ptAttribNamedActivator(ptAttribActivator):
    def getdef(self):
        # get attribute as a string, then we will turn it into an activator later
        return (self.id,self.name,4,self.value)

# Responder attribute (pick responder types box)
class ptAttribNamedResponder(ptAttribResponder):
    def getdef(self):
        # get attribute as a string, then we will turn it into an responder later
        return (self.id,self.name,4,self.value)

# DynamicText attribute pick button
class ptAttribDynamicMap(ptAttribute):
    def __init__(self,id,name=None,netForce=0):
        ptAttribute.__init__(self,id,name)
        self.value = None
        self.textmap = None
        self.netForce = netForce
    # this is to set the value via method (only called if defined)
    def __setvalue__(self,value):
        # has a ptDynamicText already been made
        try:
            self.textmap.addKey(value)
        except AttributeError:
            self.textmap = ptDynamicMap(value)
            if self.netForce:
                self.textmap.netForce(1)
            self.value = self.textmap
    def getdef(self):
        return (self.id,self.name,11)

# a GUI Dialogbox attribute
class ptAttribGUIDialog(ptAttribute):
    def __init__(self,id,name=None):
        ptAttribute.__init__(self,id,name)
        self.dialog = None
        self.value = None
    def getdef(self):
        return (self.id,self.name,12)
    def __setvalue__(self,value):
        self.dialog = ptGUIDialog(value)
        self.value = self.dialog

# a Exclude region attribute
kExRegRelease = 0
kExRegClear = 1
class ptAttribExcludeRegion(ptAttribute):
    def __init__(self,id,name=None):
        ptAttribute.__init__(self,id,name)
        self.value = None
    def getdef(self):
        return (self.id,self.name,13)
    def clear(self,sender):
        if type(self.value) != type(None):
            PtExcludeRegionSet(sender,self.value,kExRegClear)
    def release(self,sender):
        if type(self.value) != type(None):
            PtExcludeRegionSet(sender,self.value,kExRegRelease)
    def enable(self):
        self.sceneobject.physics.enable()
    def disable(self):
        self.sceneobject.physics.disable()
    def clearNow(self,sender):
        if type(self.value) != type(None):
            PtExcludeRegionSetNow(sender,self.value,kExRegClear)
    def releaseNow(self,sender):
        if type(self.value) != type(None):
            PtExcludeRegionSetNow(sender,self.value,kExRegRelease)

class ptAttribWaveSet(ptAttribute):
    def __init__(self,id,name=None):
        ptAttribute.__init__(self,id,name)
        self.value = None
    def getdef(self):
        return (self.id,self.name,19)
    def __setvalue__(self,value):
        self.waveset = ptWaveSet(value)
        self.value = self.waveset

class ptAttribSwimCurrent(ptAttribute):
    def __init__(self,id,name=None):
        ptAttribute.__init__(self,id,name)
        self.value = None
        self.current = None
    def getdef(self):
        return (self.id,self.name,21)
    def __setvalue__(self,value):
        self.current = ptSwimCurrentInterface(value)
        self.value = self.current

class ptAttribClusterList(ptAttributeList):
    def __init__(self,id,name=None):
        ptAttribute.__init__(self,id,name)
        self.value = []
    def getdef(self):
        return (self.id,self.name,22)
    def __setvalue__(self,value):
        self.value.append(ptCluster(value))
    
# special class for byObject that gets the parents name of the values when someone first asks for them
class ptByAnimObject(dict):
    def __init__(self):
        dict.__init__(self)
        self.gotParentKeys = 0
    def getParentKeys(self):
        # if we haven't got the parent keys yet, then add them to the dict
        if not self.gotParentKeys:
            for anim in dict.values(self):
                # get the animation target key
                aKey = anim.getFirstKey()
                if aKey:
                    # get its parent key
                    pKey = aKey.getParentKey()
                    if pKey:
                        # only add this key once
                        if not pKey.getName() in self:
                            dict.__setitem__(self,pKey.getName(),anim)
            self.gotParentKeys = 1
    def __getitem__(self,key):
        self.getParentKeys()
        return dict.__getitem__(self,key)
    def keys(self):
        self.getParentKeys()
        return dict.keys(self)
    def get(self, key, *args):
        self.getParentKeys()
        return dict.get(self, key, *args)

# an Animation attribute
kAnimEaseNoEase = 0
kAnimEaseConstAccel = 1
kAnimEaseSpline = 2
class ptAttribAnimation(ptAttribute):
    def __init__(self,id,name=None,byObject=0,netForce=0):
        ptAttribute.__init__(self,id,name)
        self.value = None
        self.animName = ""
        self.netForce = netForce
        if byObject:
            self.byObject = ptByAnimObject()
        else:
            self.byObject = None
    # this is to set the value via method (only called if defined)
    def __setvalue__(self,value):
        # has a ptAnimation already been made
        if type(value) == type(""):
            self.animName = value
            try:
                self.animation.setAnimName(value)
                # then if there are animations by object then set those, too
                if isinstance(self.byObject,ptByAnimObject):
                    for anim in self.byObject.values():
                        anim.setAnimName(value)
            except AttributeError:
                self.animation = ptAnimation()
                self.animation.setAnimName(value)
                if self.netForce:
                    self.animation.netForce(1)
                self.value = self.animation
        elif isinstance(value,ptKey):
            try:
                self.animation.addKey(value)
            except AttributeError:
                self.animation = ptAnimation()
                self.animation.addKey(value)
                if self.netForce:
                    self.animation.netForce(1)
                self.value = self.animation
            if isinstance(self.byObject,ptByAnimObject):
                singleAnim = ptAnimation()
                singleAnim.addKey(value)
                if self.netForce:
                    singleAnim.netForce(1)
                # set name if known
                if self.animName != "":
                    singleAnim.setAnimName(self.animName)
                name = value.getName()
                self.byObject[name] = singleAnim
    def getdef(self):
        return (self.id,self.name,14)

# a Behavior attribute
class ptAttribBehavior(ptAttribute):
    "Attribute for specifying behaviors, including multistage Behaviors"
    def __init__(self,id,name=None,netForce=1,netProp=1):
        ptAttribute.__init__(self,id,name)
        self.value = None
        self.netForce = netForce
        self.netProp = netProp
    def __setvalue__(self,value):
        self.value = value
        PtSetBehaviorNetFlags(self.value, self.netForce, self.netProp)
    def getdef(self):
        return (self.id,self.name,15)
    def run(self,avatar):
        "This will run the behavior on said avatar"
        if type(self.value) != type(None):
            if self.netForce:
                self.value.netForce(1)
                avatar.avatar.netForce(1)
            avatar.avatar.runBehavior(self.value,self.netForce,self.netProp)
    def nextStage(self,avatar,transitionTime=1.0,setTimeFlag=1,newTime=0.0,dirFlag=0,isForward=1):
        "This will go to the next stage in a multi-stage behavior"
        if type(self.value) != type(None):
            if self.netForce:
                self.value.netForce(1)
                avatar.avatar.netForce(1)
            avatar.avatar.nextStage(self.value,transitionTime,setTimeFlag,newTime,dirFlag,isForward,self.netForce)
    def previousStage(self,avatar,transitionTime=1.0,setTimeFlag=1,newTime=0.0,dirFlag=0,isForward=1):
        "This will go to the next stage in a multi-stage behavior"
        if type(self.value) != type(None):
            if self.netForce:
                self.value.netForce(1)
                avatar.avatar.netForce(1)
            avatar.avatar.previousStage(self.value,transitionTime,setTimeFlag,newTime,dirFlag,isForward,self.netForce)
    def gotoStage(self,avatar,stage,transitionTime=1.0,setTimeFlag=1,newTime=0.0,dirFlag=0,isForward=1):
        "This will go to the next stage in a multi-stage behavior"
        if type(self.value) != type(None):
            if self.netForce:
                self.value.netForce(1)
                avatar.avatar.netForce(1)
            avatar.avatar.gotoStage(self.value,stage,transitionTime,setTimeFlag,newTime,dirFlag,isForward,self.netForce)
    def setLoopCount(self,stage,loopCount):
        "This will set the loop count for a stage"
        if type(self.value) != type(None):
            PtSetBehaviorLoopCount(self.value,stage,loopCount,self.netForce)

# Material texture attribute pick button
class ptAttribMaterial(ptAttribute):
    def __init__(self,id,name=None):
        ptAttribute.__init__(self,id,name)
        self.value = None
        self.map = None
    # this is to set the value via method (only called if defined)
    def __setvalue__(self,value):
        self.map = ptImage(value)
        self.value = self.map
    def getdef(self):
        return (self.id,self.name,16)

class ptAttribMaterialAnimation(ptAttribute):
    def __init__(self, id, name = None):
        ptAttribute.__init__(self, id, name)
        self.value = None
        self.animation = None

    def __setvalue__(self, value):
        if type(self.animation) == type(None):
            self.animation = ptAnimation()
            self.animation.addKey(value)
            self.value = self.animation
        else:
            self.animation.addKey(value)

    def getdef(self):
        return (self.id, self.name, 23)


# Sceneobject list attribute (pick multiple sceneobjects box)
class ptAttribMaterialList(ptAttributeList):
    def __init__(self,id,name=None,byObject=0,netForce=0):
        ptAttributeList.__init__(self,id,name)
        self.value = []     # start as an empty list
        self.map = self.value
        self.netForce = netForce
        if byObject:
            self.byObject = {}
        else:
            self.byObject = None
    def getdef(self):
        return (self.id,self.name,6)
    def __setvalue__(self,value):
        if self.netForce:
            value.netForce(1)
        self.value.append(value)
        if type(self.byObject) == type({}):
            name = value.getName()
            self.byObject[name] = value

# a GUI PopUpMenu attribute
class ptAttribGUIPopUpMenu(ptAttribute):
    def __init__(self,id,name=None):
        ptAttribute.__init__(self,id,name)
        self.value = None
    def getdef(self):
        return (self.id,self.name,17)
    def __setvalue__(self,value):
        self.menu = ptGUIPopUpMenu(value)
        self.value = self.menu

# a GUI Skin attribute
class ptAttribGUISkin(ptAttribute):
    def __init__(self,id,name=None):
        ptAttribute.__init__(self,id,name)
        self.value = None
    def getdef(self):
        return (self.id,self.name,18)
    def __setvalue__(self,value):
        self.skin = ptGUISkin(value)
        self.value = self.skin

# a Grass Shader attribute
class ptAttribGrassShader(ptAttribute):
    def __init__(self,id,name=None):
        ptAttribute.__init__(self,id,name)
        self.value = None
    def getdef(self):
        return (self.id,self.name,24)
    def __setvalue__(self,value):
        self.shader = ptGrassShader(value)
        self.value = self.shader


#
# ptModifier  - class for creating a Plasma modifier, such as a responder

# base class
class ptModifier:
    def __init__(self):
        self.key = None
        self.SDL = None
        self.version = 0

class ptResponder(ptModifier):
    # this modifier will get a plNotifyMsg as an OnNotify
    def __init__(self):
        ptModifier.__init__(self)
        self.sceneobject = None

class ptMultiModifier(ptModifier):
    # this modifier can be attached to multiple object, but only one module
    def __init__(self):
        ptModifier.__init__(self)
