import js
import cshelperS 
import ai
import shutil
import string
import ffxshapesS
import ffxcustomS
import ffxstatesS
import ffxheroesS
import ffxpuppetsS

from js import *
from cshelperS import *
from ai import *
from shutil import *
from ffxshapesS import *
from ffxcustomS import *
from ffxstatesS import *
from ffxpuppetsS import *

############################ CONFIGURATION SECTION ########################################

FFX_SAVEGAME=1

#EP costs for certain activities
FFX_EP_SHAPESHIFT=33
FFX_EP_MIMIC=33
FFX_EP_EXPLODE=50
FFX_EP_LIVINGLASER=33
FFX_EP_GAS=33
FFX_EP_SPIN=50
FFX_EP_ICE=33
FFX_EP_SAND=33
FFX_EP_ABSORB=25
FFX_EP_SCAN=25
FFX_EP_PEACEFIELD=50
FFX_EP_LIFTTK=25
FFX_EP_THROWTK=25
FFX_EP_MOVETK=10
FFX_EP_THROWMAG=33
FFX_EP_EARTHWALL=33
FFX_EP_EARTHSPIKE=50
FFX_EP_ANIMATE=33
FFX_EP_SUSTAIN=10
FFX_EP_MINDSHIELD=25
FFX_EP_PHYSICALSHIELD=50

#the range at which various powers kick in
FFX_ABSORBRANGE=5
FFX_MIMICRANGE=5
FFX_DRAINRANGE=30
FFX_SHIELDRANGE=30

#variable forgotten about in FF API, but makes code more readable
PCSTATE_EXILE=3

#add voice ID's here to allow for gender determination
girls=('SU','AL','EV','BB','LW','IQ','RB','GB','SB','F1','DF','SH')
beasts=('SA','WA','RP','TR','ML')
mechs=('MW','MC','FM')
aliens=('AW','PR')

###########################################################################################

builtinMimics=['devil_doll']

mimicSkin='strangers/art/custom_characters/female_basic/skins/devildoll/female_basic.tga'

ffxAttribs=[]
ffxMeshes=[]
templates=[]
throwObjects=[]
associations=[]

FFX_RPG_DEPRESSED_CHANCE=5
FFX_PRESENCE_CHANCE=5
FFX_JINX_CHANCE_JAM=10
FFX_JINX_CHANCE_SLIP=10
FFX_PHEREMONE_CHANCE=5
FFX_MAGNET_FORCE=30000
FFX_SCARY_CHANCE=10

######################### INITIALISATION  ###############################################################

FFX_STATE_GAS=1
FFX_STATE_SAND=2
FFX_STATE_ICE=3
FFX_STATE_SPIN=4
FFX_STATE_ENERGYSHIELD=5
FFX_STATE_SHRUNK=6
FFX_STATE_TRANSMUTED=7
FFX_STATE_PUPPET=800
FFX_STATE_UNINITIALISED=100

FFX_GENDER_GOOD=0
FFX_GENDER_EVIL=32

#start up the Expansion Pack!
def initialise(skirmish=0):
    global FFX_SAVEGAME
    FFX_SAVEGAME=0
    #save the info on who was taken in a failsafe save-game safe manner
    storeHeroes()
    global FFX_RULES_FROZENDAMAGE
    Mission_SetAttr('hiders',0)
    if FFX_SETTINGS_EXPANSION==0:
        return
    print 'initialising FFX: skirmish=%d'%(skirmish)
    Mission_SetAttr('ffxSkirmish',skirmish)
    #rude hack to preserve play balance in original FF campaign
    if (cshelperS.getMission()<=4) & (Object_Exists('minute_man') | Object_Exists('minute_man_neutral')):
        FFX_RULES_FROZENDAMAGE=0
    FFX_InitStates()
#    FFX_CrappyPrecache()
    Mission_SetAttr('shapeReady',0)
    Mission_SetAttr('dummyCount',1)
    Mission_SetAttr('tigCount',1)
    Mission_SetAttr('formCount',1)
    Mission_SetAttr('pickupReged',0)
    Mission_SetAttr('shapechanged',0)
    Mission_SetAttr('teleportIndex',0)
    Global_RegisterAttr('ffxState',FFX_STATE_UNINITIALISED)
#    Global_RegisterAttr('heroIndex',0)
    Global_RegisterAttr('templateID',0)
    Global_RegisterAttr('fxHandle',-1)
    Global_RegisterAttr('secondTimer',0)
    Global_RegisterAttr('gender',FFX_GENDER_UNKNOWN)
    Global_RegisterAttr('baseStrength',-1)
    Global_RegisterAttr('baseSpeed',-1)
    for obj in Mission_GetDynamicObjects():
        if (Object_GetClass(obj)&OC_CHARACTER)!=0:
            animateForMesh(obj)
    RegTimer('animate1',0.2)
    RegTimer('animate2',0.5)
    RegTimer('parseLogFile',0.8)
    RegTimer('initAttribs',1)
    fillTemplateList()
    for hero in GetAllHeroes():
        if FFX_WHOAMI!=0:
            Mission_CustomAction('CUSTOM_WHOAMI','',hero,'OnWho',5,0)

def animateForMesh(char):
    AI_Animate(char,Object_GetTemplate(char))
    RegTimer('animate1',0.2,0,char)
    RegTimer('animate2',0.5,0,char)

def animate1(event):
    AI_Animate(event.object,'melee_idle')

def animate2(event):
    AI_Animate(event.object,'idle')

def OnWho(dummy,char):
    Mission_StatusText('I am %s'%(Object_GetTemplate(char)))

def FFX_CrappyPrecache():
    y=-1000
    index=1
#    for template in transmuteTargets:
#        y=y-32
#        name='precache_%d'%(index)
#        index=index+1        
#        Object_SpawnAt(template,(-1000,y,0),name)
#        RegTimer('PreCacheRemove',1,0,name)
#     for pair in urbanTemplates:
#         template=pair[1]
#         y=y-32
#         name='precache_%d'%(index)
#         index=index+1        
#         Object_SpawnAt(template,(-1000,y,0),name)
#         RegTimer('PreCacheRemove',1,0,name)
#     for pair in natureTemplates:
#         template=pair[1]
#         y=y-32
#         name='precache_%d'%(index)
#         index=index+1        
#        Object_SpawnAt(template,(-1000,y,0),name)
#        RegTimer('PreCacheRemove',1,0,name)
    template='ffx_crystalprison'
    y=y-32
    name='precache_%d'%(index)
    index=index+1        
    Object_SpawnAt(template,(-1000,y,0),name)
    RegTimer('PreCacheRemove',1,0,name)		    

def PreCacheRemove(event):
    Object_Destroy(event.object)

def FFX_InitStates():
    if FFX_RULES_FROZENDAMAGE:
        RPG_ICE_HP=10
    else:
        RPG_ICE_HP=15
    dst='data/art/library/fx/char_states/blind'
    if (FFX_STATE_BLIND=='none') | (FFX_SETTINGS_STATES==0):
        src='ffx/fx/blind'
    elif FFX_STATE_BLIND=='dizzy':
        src='ffx/fx/dizzy'
    FFX_Replace(dst,src)
    dst='data/art/library/fx/char_states/hexed'
    if (FFX_STATE_HEX=='none') | (FFX_SETTINGS_STATES==0):
        src='ffx/fx/hex'
    elif FFX_STATE_HEX=='crystallised':
        src='ffx/fx/crystallised'
    elif FFX_STATE_HEX=='shrunk':
        src='ffx/fx/shrunk'
    FFX_Replace(dst,src)

def FFX_Replace(dst,src):
    print "preparing to copy '%s' over '%s'"%(src,dst)
    try:
       shutil.rmtree(dst)
       print "'%s' removed"%(dst)
    except:
       print "cant remove '%s'"%(dst)
    try:
       shutil.copytree(src,dst)
       print "'%s' copied over '%s'"%(src,dst)
    except:
       print "cant copy '%s' to '%s'"%(src,dst)
    
def getGender(char):
    template=Object_GetTemplate(char)
    try:
        voice=Template_GetSpeechID(template)
    except:
        return FFX_GENDER_MALE
    for v in beasts:
        if v==voice:
            return FFX_GENDER_ANIMAL
    for v in mechs:
        if v==voice:
            return FFX_GENDER_ROBOT
    for v in aliens:
        if v==voice:
            return FFX_GENDER_ALIEN
    for v in girls:
        if v==voice:
            return FFX_GENDER_FEMALE
    return FFX_GENDER_MALE	
	
def setGender(char):
    if Object_GetAttr(char,'gender')==FFX_GENDER_UNKNOWN:
        gender=getGender(char)
        if ((Object_GetClass(char)&OC_MINION)!=0) | ((Object_GetClass(char)&OC_VILLIAN)!=0):
            gender=gender+FFX_GENDER_EVIL		
        Object_SetAttr(char,'gender',gender)

def parseLogFile(event):
    global ffxAttribs
    global ffxMeshes
    try:
        fp = open('ff.log', 'r')
        name=fp.readline()
        while name!='':
            name=fp.readline()     
            words=string.split(name)
            #flush if we find an initialising physics line
            if len(words)>=4:
                if (words[2]=='initializing') & (words[3]=='physics'):
                    print 'clearing list'
                    ffxAttribs=[]
                    ffxMeshes=[]
            #store template-attribute pairs
            if (len(words)>=9):
                if (words[5]=='has') & (words[6]=='unknown') & (words[7]=='attribute'):
                    #print '%s : %s'%(words[4],words[8])
                    #check for duplicates
                    dupe=0
                    for attrib in ffxAttribs:
                        if (attrib[0]==words[4]) & (attrib[1]==words[8]):
                            dupe=1
                    if dupe==0:
                        ffxAttribs.append((words[4],words[8]))
                if (words[3]=='animation') & (words[4]=='sequence'):
                    template=words[5]
                    template=template[1:][:-1]
                    mesh=words[9]
                    mesh=mesh[:-12]
                    #print '%s has mesh %s'%(template,mesh)
                    dupe=0
                    for meshPair in ffxMeshes:
                        if (meshPair[0]==template) & (meshPair[1]==mesh):
                            dupe=1
                    if dupe==0:
                        ffxMeshes.append((template,mesh))
        fp.close()
        print 'THESE ARE THE ATTRIBUTES:'
        print ffxAttribs
        print 'THESE ARE THE MESHES:'
        print ffxMeshes
    except:
        print 'cannot open ff.log'
        RegTimer('parseLogFile',3)
        Mission_StatusText('Cant find log file: do you have -log in your shortcut?')
        Mission_StatusText('Use the Freedom Force X shortcut provided if you dont know.')

def initAttribs(event):
    #code here to determine who can change into what
    masters=[]
    slaves=[]
    #identify which heros have the 'modified' attributes
    for object in js.Mission_GetDynamicObjects():
        if (Object_GetClass(object) & js.OC_CHARACTER):
            initAttribsForChar(object)
    RegTimer('updateAttribs5',0.5)
    RegTimer('updateAttribs1',1)
    RegTimer('updateStates',1.25)

def updateStates(event):
    RegTimer('updateStates',0.5)
    if cshelperS.isPlaying():
        return
    for char in Mission_GetDynamicObjects():
        if Object_GetClass(char)&OC_CHARACTER:
            #prevent shrunken players from breaking the spell
            if (Object_GetClass(char)&OC_CONTROLLABLE)!=0:
                if Object_GetAttr(char,'ffxState')==FFX_STATE_SHRUNK:
                    Object_SetSecondaryState(char,SCSTATE_INVISIBLE,1,0)
                    Trigger_Move(char,'lock')
                #and make sure TK ers drop their stuff when stunned etc and get commands for throwing at the right time.
                if hasAttribute(char,'telekinetic') | hasAttribute(char,'supertk') | hasAttribute(char,'magnetic') | hasAttribute(char,'earthcontrol'):
                    updateTK(char)
            #check the stasis thing:
            if FFX_RULES_STASIS:
                if Object_GetPrimaryState(char)==PCSTATE_STATIC:
                    savingThrow=randint(6,9)
                    if Object_GetAttr(char,'strength')>=savingThrow:
                        Object_SetPrimaryState(char,PCSTATE_STATIC,0,REMOVE_STATE)
                        #AI_Animate(char,'power')
            #and the speed-force thing
            if FFX_RULES_SPEEDFORCE:
                if (Object_GetSecondaryStates(char)&SCSTATE_ENERGIZED)!=0:
                    Object_SetAttr(char,'speed',9)
                elif (Object_GetAttr(char,'ffxState')!=FFX_STATE_SPIN) & (Object_GetAttr(char,'baseSpeed')!=-1) & (Object_GetAttr(char,'baseSpeed')<Object_GetAttr(char,'speed')):
                    Object_SetAttr(char,'speed',Object_GetAttr(char,'baseSpeed'))
            if FFX_SETTINGS_STATES==0:
                return
			#swap over/augment some character state stuff
            if Object_GetSecondaryStates(char)&SCSTATE_BLIND:
                if swapStates(FFX_STATE_BLIND,char):
                    Object_SetSecondaryState(char,SCSTATE_BLIND,0,REMOVE_STATE)
            if Object_GetSecondaryStates(char)&SCSTATE_AREA_ENERGIZED:
                if swapStates(FFX_STATE_ENERGISED,char):
                    Object_SetSecondaryState(char,SCSTATE_AREA_ENERGIZED,0,REMOVE_STATE)
            if (Object_GetPrimaryState(char)==PCSTATE_EXILE) & (Object_GetAttr(char,'ffxState')!=FFX_STATE_TRANSMUTED):
                print 'checking exiled dude %s'%(char)
                if swapStates(FFX_STATE_EXILE,char):
                    if Object_GetAttr(char,'ffxState')!=FFX_STATE_TRANSMUTED:
                        Object_SetPrimaryState(char,PCSTATE_EXILE,0,REMOVE_STATE)
            if Object_GetSecondaryStates(char)&SCSTATE_HEXED:
                print '%s is HEXED!'%(char)
                if swapStates(FFX_STATE_HEX,char):
                    Object_SetSecondaryState(char,SCSTATE_HEXED,0,REMOVE_STATE)
            else: #eep! eep! density control detected!
                if Object_GetAttr(char,'speed')<Object_GetAttr(char,'baseSpeed'):
                    if swapStates(FFX_STATE_DENSITY,char):
                        Object_SetGenericState(char,OBJSTATE_DENSITY_CHANGE,0,REMOVE_STATE)

#applies the new state for the given switch                                
def swapStates(oldState,char):
    #print 'swapStates: char=%s state=%s'%(char,oldState)
    removeState=0
    if oldState=='none':
        removeState=0
    elif oldState=='dizzy':
        if Object_IsAlive(char):
            thing=randint(1,10)
            if (thing==1):
                Trigger_Force(char,1,0,0,Object_GetAttr(char,'minForce'),TF_ABSOLUTE)
            elif (thing==2):
                pos=Get_ObjectPos(char)
                pos2=(pos[0]+randint(-50,50),pos[1]+randint(-50,50),pos[2])
                Trigger_Move(char,'move',pos2)
    elif oldState=='crystallised':
        makeCrystallised(char,20)
        removeState=1
    elif oldState=='shrunk':
        Object_SetSecondaryState(char,SCSTATE_INVISIBLE,10,0)
        Trigger_Move(char,'lock')
        AI_SetEnabled(char,0)
        RegTimer('OnUnShrink',10,0,char)
        setFFXState(char,FFX_STATE_SHRUNK)
        removeState=1
    elif oldState=='up':
        Trigger_Force(char,0,0,1,100000,TF_ABSOLUTE)
        RegTimer('OnGravityUp',0.8,0,char)
        removeState=1
    elif oldState=='down':
        currentStr=Object_GetAttr(char,'strength')
        newStr=currentStr-randint(3,5)
        print '%s has strength of %d'%(char,newStr)
        if newStr<-5:
            newStr=-5
        Object_SetAttr(char,'strength',newStr)
        if (newStr<0):
            if Object_GetPrimaryState(char)!=PCSTATE_STATIC:
                AI_Animate(char,'land_back','OnGravityDown')
            else:
                RegTimer('OnGravityDown',0.1,0,char)
        removeState=1
    elif oldState=='transmute':
        if Object_GetPrimaryState(char)!=PCSTATE_EXILE:
            RegTimer('OnExile',0.1,0,char)
        obj='trans_%s'%(char)
        if Object_Exists(obj)==0:
            template=transmuteTargets[randint(0,len(transmuteTargets)-1)]
            print 'turning %s into %s'%(char,template)
            Object_SpawnAt(template,Get_ObjectPos(char),obj)
            RegCharState(char,PCSTATE_NORMAL,'DeTransmute')
            setFFXState(char,FFX_STATE_TRANSMUTED)
            Object_SetSecondaryState(char,SCSTATE_INVISIBLE,100,0)
            RegTimer('Transmute2',0.15,0,obj,char)
        removeState=1
    return removeState

def OnExile(event):
    Object_SetPrimaryState(event.object,PCSTATE_EXILE,10,0)

def Transmute2(event):
    obj=event.object
    char=event.string
    Object_SetAttr(obj,'maxHealth',Object_GetAttr(char,'maxHealth'))
    Object_SetAttr(obj,'health',Object_GetAttr(char,'health'))
    RegDeath(obj,'TransmuteKill')	                

def setFFXState(char,state):
    myState=Object_GetAttr(char,'ffxState')
    if myState>800:
        Object_SetAttr(char,'ffxState',800+state)
    else:
        Object_SetAttr(char,'ffxState',state)

def TransmuteKill(event):
    char=event.object[6:]
    Object_SetSecondaryState(char,SCSTATE_INVISIBLE,0,REMOVE_STATE)
    Object_SetPrimaryState(char,PCSTATE_EXILE,0,REMOVE_STATE)
    Trigger_Damage(char,Object_GetAttr(char,'health')+5)

def DeTransmute(event):
    char=event.object
    setFFXState(char,0)
    obj='trans_%s'%(char)
    Object_SetSecondaryState(char,SCSTATE_INVISIBLE,0,REMOVE_STATE)
    print 'DeTransmute: state(%s)=%d'%(char,Object_GetPrimaryState(char))
    if Object_Exists(obj)==0:
        Trigger_Damage(char,Object_GetAttr(char,'health')+5)
    else:
        if distanceSq(char,obj)>300:
            Object_SetAttr(char,'maxHealth',Object_GetAttr(obj,'maxHealth'))
            Object_SetAttr(char,'health',Object_GetAttr(obj,'health'))
            #basically move the character to the site of the object
            FFX_Teleport(char,Get_ObjectPos(obj))
            index=Mission_GetAttr('teleportIndex')+1
            RegTimer('tp2',1,index,char,Object_GetTemplate(char))
        else:
            Object_SetAttr(char,'maxHealth',Object_GetAttr(obj,'maxHealth'))
            damage=Object_GetAttr(char,'health')-Object_GetAttr(obj,'health')
            Trigger_Damage(char,damage)
            Object_SetPrimaryState(char,PCSTATE_STUNNED,1,0)
        Object_Destroy(obj)
		   
def OnUnShrink(event):
    char=event.object
    Trigger_Move(char,'unlock')
    AI_SetEnabled(char,1)
    Object_SetSecondaryState(char,SCSTATE_INVISIBLE,0,REMOVE_STATE)
    fallOver(char)
    setFFXState(char,0)
						                    
def OnGravityUp(event):
    char=event.object
    print 'OnGravityUp:%s'%(char)
    Object_SetPrimaryState(char,PCSTATE_STATIC,5,0)

def OnGravityDown(event):
    char=event.object
    duration=-Object_GetAttr(char,'strength')*3
    Object_SetPrimaryState(char,PCSTATE_STATIC,duration,0)

def makeCrystallised(char,intensity):
    #check if they already have a crystal
    crystalName='crys_%s'%(char)
    if Object_Exists(crystalName):
        Object_SetAttr(crystalName,'health',intensity)
    else:
        Trigger_Move(char,'lock')
        pos=Get_ObjectPos(char)
        Object_SpawnAt('ffx_crystalprison',(pos[0],pos[1],pos[2]+10),crystalName)
        Object_SetAttr(crystalName,'health',intensity)
        if (Object_GetClass(char)&OC_CONTROLLABLE)==0:
            print 'adding kill goal to %s'%(char)
            goal=ai.goal.KillObjects(crystalName)
            addGoalToAI(char, goal, PRI_ALWAYS+1)
        RegDeath(crystalName,'OnCrystalDown')
        js.Event_RegisterSink(EVENT_OBJ_PROXIMITY_LEAVE, 'OnCrystalLeave', char, crystalName, 2)

def OnCrystalDown(event):
    crystal=event.object
    length=len(crystal)
    char=crystal[5:]
    print 'crystal down (%s): checking for character %s'%(crystal,char)
    if Object_Exists(char):
        Trigger_Move(char,'unlock')
        fallOver(char)

def OnCrystalLeave(event):
    print 'OnCrystalLeave: obj=%s str=%s'%(event.object,event.string)
    Trigger_Damage(event.string,Object_GetAttr(event.string,'health')+5)

def OnAddTo(slave,master):
    set=getShapeSet(Object_GetTemplate(master))
    if len(set)==0:
        set.append(Object_GetTemplate(master))
        set.append(Object_GetTemplate(slave))
        shapeChangerSets.append(set)
    else:
        set.append(Object_GetTemplate(slave))
    print shapeChangerSets
    writeSets()

def OnClearList(master,dummy):
    set=getShapeSet(Object_GetTemplate(master))
    if len(set)>0:
        shapeChangerSets.remove(set)
    writeSets()
 
def writeSets():
    fp = open('data/missions/scripts/ffxshapes.py', 'w')
    fp.write('shapeChangerSets=[\n')
    for set in shapeChangerSets:
        fp.write('[')
        for template in set:
            fp.write("'%s',"%(template))
        fp.write('],\n')
    fp.write(']')   
    fp.close()

def hasAttribute(char,attrib):
    for pair in ffxAttribs:
        if (pair[0]==Object_GetTemplate(char)) & (pair[1]==attrib):
            return 1
    return 0

def initAttribsForChar(char,oldName=0):
    print 'initAttribs on %s'%(char)
    template=Object_GetTemplate(char)
    Object_SetAttr(char,'ffxState',0)
    for attrib in ffxAttribs:
        if attrib[0]==template:
            init="init%s('%s',%d)"%(attrib[1],char,oldName)
            try:
                exec init
            except:
                print init
                print 'error in initialising attribute %s'%(attrib[1])
                Mission_StatusText('error in initialising attribute %s'%(attrib[1]))

def initAttribsEvent(event):
    initAttribsForChar(event.object,event.user)

#given a character's template, find their entry (if any) in customisation array
def getByTemplate(char,array,index):
    template=Object_GetTemplate(char)
    match=0
    for assoc in associations:
        if assoc[0]==template:
            match=1
            template=assoc[1]
    if match==0:
        associate(char,template)
    result=array[0][index]

    for elem in array:
        if elem[0]==template:
            result=elem[index]
    return result

#associate the characters template with known hero file data
def associate(char,template):
    global associations
    for hero in ffxheroesS.ffxHeroes:
        #print '%s vs %s : (%s:%s) (%d:%d) (%d:%d)'%(hero[0],template,hero[1],Template_GetSpeechID(template),hero[2],Object_GetAttr(char,'baseStrength'),hero[3],Object_GetAttr(char,'baseSpeed'))
        if Template_GetSpeechID(template)==hero[1]:
            #print 'voice ID matches'
            if Object_GetAttr(char,'baseStrength')==hero[2]:
                #print 'baseStrength matches'
                if Object_GetAttr(char,'baseSpeed')==hero[3]:
                    #print 'baseSpeed matches'
                    mismatch=0
                    if hero[6]!='':
                        if hasAttribute(char,hero[6])==0:
                            mismatch=1
                    if hero[7]!='':
                        if hasAttribute(char,hero[7])==0:
                            mismatch=1
                    if hero[8]!='':
                        if hasAttribute(char,hero[8])==0:
                            mismatch=1
                    if hero[9]!='':
                        if hasAttribute(char,hero[9])==0:
                            mismatch=1
                    if hero[10]!='':
                        if hasAttribute(char,hero[10])==0:
                            mismatch=1
                    #dont check if the character has flesh type in their hero file - it may be they have unstable molecules
                    if (hero[5]!=0) & (hero[5]!=Object_GetAttr(char,'material')):
                        mismatch=1
                    if mismatch==0:
                        associations.append(template,hero[0])
 
###################### FIND THE TEMPLATES OF ALL PROP OBJECTS ##################################

#the tuples stored in here are template, mass and material
#if the object isnt throwable, mass gets listed as :

FFX_TOO_HEAVY=10000000

def fillTemplateList():
    for i in range(1,1000):
        obj='_impobj_%d'%(i)
        if Object_Exists(obj):
            if (Object_GetClass(obj)&OC_CHARACTER)==0:
                if Object_GetAttr(obj,'throwable')!=0:
                    throwObjects.append(obj)
                temp=Object_GetTemplate(obj)
                match=0
                for template in templates:
                    if temp==template[0]:
                        match=1
                if match==0:
                    mass=FFX_TOO_HEAVY
                    if isTKable(obj):
                        mass=Object_GetAttr(obj,'mass')    
                    templates.append((temp,mass,Object_GetAttr(obj,'material')))
    #special case for debris
    templates.append('building_debris_0',150,1)
    for obj in Mission_GetDynamicObjects():
        if (Object_GetClass(obj)&OC_CHARACTER)==0:
            temp=Object_GetTemplate(obj)
            match=0
            for template in templates:
                if temp==template[0]:
                    match=1
            if match==0:
                mass=FFX_TOO_HEAVY
                if Object_GetAttr(obj,'throwable')==1:
                    mass=Object_GetAttr(obj,'mass')    
                templates.append((temp,mass,Object_GetAttr(obj,'material')))

###################### UPDATE #############################################################

def GetAllHeroes():
    objects = js.Mission_GetDynamicObjects()
    heroes = []
    for object in objects:
        if (Object_GetClass(object) & js.OC_CONTROLLABLE)!=0:
            heroes.append(object)
    return heroes

def OnDie(event):
    obj=event.object
    damage=Object_GetAttr(obj,'health')
    if damage<1:
        damage=1
    Trigger_Damage(obj,damage)

def updateAttribs1(event):
    symbiotes=[]
    parasites=[]
    #savegame check
    restoreMimics=0
    global FFX_SAVEGAME
    if FFX_SAVEGAME!=0:
        print 'mid level save game detected'
        restoreMimics=1
        FFX_SAVEGAME=0
    RegTimer('updateAttribs1',1)
    if cshelperS.isPlaying():
        return
    for obj in Mission_GetDynamicObjects():
        if (Object_GetClass(obj) & OC_CHARACTER)!=0:
            if restoreMimics:
                oclass=Object_GetClass(obj)
                if ((oclass&OC_CONTROLLABLE)!=0) & ((oclass&(OC_VILLIAN|OC_MINION|OC_POLICE|OC_CIVILIAN))!=0):
                    print 'restoring status to mimicked NPC type:%s'%(obj)
                    AI_MakeIntoHero(obj)
                    RegTimer('OnMimicMinion1',0.2,Object_GetAttr(obj,'health'),obj)
            #frozen damage
            if FFX_RULES_FROZENDAMAGE:
                if Object_GetPrimaryState(obj)==PCSTATE_FROZEN:
                    damage=randint(0,2)
                    print '%s is frozen: damage=%d'%(obj,damage)
                    Object_SetAttr(obj,'health',Object_GetAttr(obj,'health')-damage)
                    if Object_GetAttr(obj,'health')<1:
                        Object_SetAttr(obj,'health',1)
                        Trigger_Damage(obj,1)
                        RegTimer('OnDie',3,0,obj)
            if Object_GetAttr(obj,'baseStrength')==-1:
                Object_SetAttr(obj,'baseStrength',Object_GetAttr(obj,'strength'))
                Object_SetAttr(obj,'baseSpeed',Object_GetAttr(obj,'speed'))
            #deal with gravitroned characters here
            str=Object_GetAttr(obj,'strength')
            bStr=Object_GetAttr(obj,'baseStrength')
            if str<bStr:
               if str<0:
                    damage=-(str*0.4)
                    print '%s is gravitied: damage=%d'%(obj,damage)
                    Object_SetAttr(obj,'health',Object_GetAttr(obj,'health')-damage)
                    if Object_GetAttr(obj,'health')<1:
                        Object_SetAttr(obj,'health',1)
               str=str+0.33
               Object_SetAttr(obj,'strength',str)
            #beat battery powered character's energy back down to nearest integer
            #to stop the slow accumulation of EP
            if Object_GetAttr(obj,'maxEnergyPoints')>100:
                intEP=10*int(Object_GetAttr(obj,'energyPoints')/10)
                Object_SetAttr(obj,'energyPoints',intEP)
            if hasAttribute(obj,'superhealer'):
                updateSuperHealer(obj)
            if hasAttribute(obj,'degenerative'):
                updateDegenerative(obj)
            #sensitive characters can see at all times
            if (Object_GetClass(obj)&OC_CONTROLLABLE)!=0:
                if hasAttribute(obj,'sensitive'):
                    Mission_SetDetectionEnabled(0)
            #deal with characters in the various different states
            state=Object_GetAttr(obj,'ffxState')
            if hasAttribute(obj,'spinner'):
                updateSpinner(obj)
            metamorphic=hasAttribute(obj,'metamorphic')
            if hasAttribute(obj,'gaseousform') | metamorphic:
                updateGaseousForm(obj)
            if hasAttribute(obj,'sandstorm') | metamorphic:
                updateSandstorm(obj)
            if hasAttribute(obj,'icecloud') | metamorphic:
                updateIceCloud(obj)
            if hasAttribute(obj,'metashield'):
                updateMShield(obj)
            #symbiosis/interference check
            if hasAttribute(obj,'symbiote'):
                symbiotes.append(obj)
            if hasAttribute(obj,'parasite'):
                parasites.append(obj)
            if hasAttribute(obj,'ephemeral'):
                updateEphemeral(obj)
            if hasAttribute(obj,'heavyfooted'):
                heavyfootUpdate(obj)
    for c1 in symbiotes:
        for c2 in symbiotes:
            if c1!=c2:
                if distanceSq(c1,c2)<1000:
                    Object_SetSecondaryState(c1, SCSTATE_ENERGIZED, 5, 0)
                    Object_SetSecondaryState(c2, SCSTATE_ENERGIZED, 5, 0)
    for c1 in parasites:
        for c2 in parasites:
            if c1!=c2:
                if distanceSq(c1,c2)<3000:
                    Object_SetSecondaryState(c1, SCSTATE_POWER_NULLIFICATION, 5, 0)
                    Object_SetSecondaryState(c2, SCSTATE_POWER_NULLIFICATION, 5, 0)
    updatePuppets()

#5 second intervals        
def updateAttribs5(event):
    global shapeChangerSets
    RegTimer('updateAttribs5',5)
    if cshelperS.isPlaying():
        return
    redoShapes=0
    slaves=[]
    masters=[]
    for obj in Mission_GetDynamicObjects():
        setGender(obj)
        if Object_GetClass(obj) & OC_CHARACTER:
            if Object_GetAttr(obj,'baseStrength')==-1:
                Object_SetAttr(obj,'baseStrength',Object_GetAttr(obj,'strength'))
            if Object_GetAttr(obj,'baseSpeed')==-1:
                Object_SetAttr(obj,'baseSpeed',Object_GetAttr(obj,'speed'))
            if Object_GetAttr(obj,'ffxState')==FFX_STATE_UNINITIALISED:
                if getMesh(Object_GetTemplate(obj))=='':
                    animateForMesh(obj)
                    RegTimer('parseLogFile',0.4)
                else:
                    parseLogFile(event)
                RegTimer('initAttribsEvent',1,0,obj)
            #custom commands that need refreshing for new characters on the scene
            if Object_GetClass(obj) & OC_CONTROLLABLE:
                if (obj[:4]!='form') & hasAttribute(obj,'tempform'):
                    slaves.append(obj)
                if (obj[:4]!='form') & hasAttribute(obj,'shapeshifter'):
                    masters.append(obj)
                #checks for new objects to add commands to basically
                if hasAttribute(obj,'mimic'):
                    initmimic(obj,1)
                if hasAttribute(obj,'guardianm'):
                    initguardianm(obj,1) 
                if hasAttribute(obj,'guardianp'):
                    initguardianp(obj,1)
                if hasAttribute(obj,'livinglaser'):
                    initlivinglaser(obj,1)
                if hasAttribute(obj,'telekinetic'):
                    inittelekinetic(obj,1)
                if hasAttribute(obj,'supertk'):
                    initsupertk(obj,1)
                if hasAttribute(obj,'earthcontrol'):
                    initearthcontrol(obj,1)
                if hasAttribute(obj,'magnetic'):
                    initmagnetic(obj,1)
                if hasAttribute(obj,'scary'):
                    initscary(obj)
            #reg 21 events that need refreshing for new characters on the scene
            if hasAttribute(obj,'absorber'):
                initabsorber(obj,1)
            if hasAttribute(obj,'bloodsucker'):
                initbloodsucker(obj,1)
            #deal with attributes that have a regular chance of doing something
            if Object_IsAlive(obj):
                if hasAttribute(obj,'depressed'):
                    if randint(1,100) < FFX_RPG_DEPRESSED_CHANCE:
                        AI_Animate(obj,'stunned')
                if hasAttribute(obj,'presence'):
                    updatePresence(obj)
                if hasAttribute(obj,'absorber'):
                    updateAbsorber(obj)
                if hasAttribute(obj,'jinx'):
                    updateJinx(obj)
                if hasAttribute(obj,'pheremones'):
                    updatePheremones(obj)
                if hasAttribute(obj,'scary'):
                    updateScary(obj)
                if hasAttribute(obj,'vunstablemols'):
                    if randint(1,2)==1:
                        Object_SetAttr(obj,'material',randint(1,10))
                if hasAttribute(obj,'biohazard'):
                    Trigger_Explosion(obj,'ffx_biohazard')
                if hasAttribute(obj,'frostbite') | ((Object_GetTemplate(obj)=='chaosgirl') & (Object_GetAttr(obj,'material')==9)):
                    Trigger_Explosion(obj,'ffx_frostbite',-1)
                if hasAttribute(obj,'overheated') | ((Object_GetTemplate(obj)=='chaosgirl') & (Object_GetAttr(obj,'material')==4)):
                    Trigger_Explosion(obj,'ffx_overheated',-1)
                if hasAttribute(obj,'shortcirc'):
                    Trigger_Explosion(obj,'ffx_shortcircuit',-1)
    Mission_SetAttr('shapeReady',1)
    if (Mission_GetAttr('shapechanged')==0):
        if len(masters)>0:
            Mission_SetAttr('shapechanged',1)
        if len(slaves)>0:
            for master in masters:
                newset=[Object_GetTemplate(master)]
                set=getShapeSet(Object_GetTemplate(master))
                if len(set)>0:
                    shapeChangerSets.remove(set)
                for slave in slaves:
                    newset.append(Object_GetTemplate(slave))
                shapeChangerSets.append(newset)       
            if Mission_GetAttr('ffxSkirmish')==0:
                writeSets()
        for master in masters:
            initshapeshifter(master)
        for slave in slaves:
            Object_Destroy(slave)
            print 'destroying %s(%s)'%(slave,Object_GetTemplate(slave))

#put code in here thats required if a character has been destroyed and re-spawned (say by living laser/mimic/dispersed form etc.)    
#def reInit(char,unPickle=0):
#    if (unPickle==0) & (FFX_WHOAMI!=0):
#        Mission_CustomAction('CUSTOM_WHOAMI','',char,'OnWho',5,0)
#    #Event Registering that needs re-doing
#    if hasAttribute(char,'dispersed'):
#        initdispersed(char,unPickle) 
#    if hasAttribute(char,'bloodsucker'):
#        initbloodsucker(char,unPickle) 
#    if hasAttribute(char,'telekinetic'):
#        inittelekinetic(char,unPickle) 
#    if hasAttribute(char,'supertk'):
#        initsupertk(char,unPickle) 
#    if hasAttribute(char,'metashield'):
#        initmetashield(char,unPickle) 
#    if hasAttribute(char,'metashieldp'):
#        initmetashieldp(char) 
#    if hasAttribute(char,'frostbite'):
#        initfrostbite(char) 
#    if hasAttribute(char,'overheated'):
#        initoverheated(char) 
#    if hasAttribute(char,'shortcirc'):
#        initshortcirc(char) 
#    if hasAttribute(char,'earthcontrol'):
#        initearthcontrol(char,unPickle) 
#    if hasAttribute(char,'magnetic'):
#        initmagnetic(char,unPickle)
#    if hasAttribute(char,'puppetu'):
#        initpuppetu(char,unPickle)
#    if hasAttribute(char,'puppetn'):
#        initpuppetn(char,unPickle)
#    if hasAttribute(char,'mindless'):
#        initmindless(char) 
#    if hasAttribute(char,'mindless'):
#        initmindless(char)
#    if hasAttribute(char,'fearless'):
#        initfearless(char)
#    if hasAttribute(char,'dispassionate'):
#        initdispassionate(char)  
#    if hasAttribute(char,'stockpile'):
#        initstockpile(char)
#    if hasAttribute(char,'blastshield'):
#        initblastshield(char)
#    if hasAttribute(char,'acidblood'):
#        initacidblood(char)
#    if hasAttribute(char,'degenerative'):
#        initdegenerative(char)
#    if hasAttribute(char,'spiny'):
#        initspiny(char)
#    if hasAttribute(char,'conduit'):
#        initconduit(char)
#    if hasAttribute(char,'hollowbones'):
#        inithollowbones(char)
#    if hasAttribute(char,'pheremones'):
#        initpheremones(char)
#    if hasAttribute(char,'battery'):
#        initbattery(char,1)
#    #custom command based ones need reinitialising after a shapechange
#    if hasAttribute(char,'metamorphic'):
#        initmetamorphic(char,unPickle) 
#    if hasAttribute(char,'guardianm'):
#        initguardianm(char,unPickle) 
#    if hasAttribute(char,'guardianp'):
#        initguardianp(char,unPickle)
#    if hasAttribute(char,'sandstorm'):
#        initsandstorm(char,unPickle)
#    if hasAttribute(char,'explosive'):
#        initexplosive(char,unPickle)
#    if hasAttribute(char,'spinner'):
#        initspinner(char,unPickle)
#    if hasAttribute(char,'gaseousform'):
#        initgaseousform(char,unPickle)
#    if hasAttribute(char,'icecloud'):
#        initicecloud(char,unPickle)
#    if hasAttribute(char,'livinglaser'):
#        initlivinglaser(char,unPickle) 
#    if hasAttribute(char,'mimic'):
#        initmimic(char,unPickle) 
#    if hasAttribute(char,'absorber'):
#        initabsorber(char,unPickle)
#    if hasAttribute(char,'shapeshifter') | hasAttribute(char,'tempform'):
#        initshapeshifter(char,unPickle)
#    if hasAttribute(char,'scary'):
#        initscary(char)
#    #set a few attributes that may be needed - band aid solution
#    Object_SetAttr(char,'tkCom',0)

##################### HELPER FUNCTIONS ###################################

def distanceSq(object1,object2):
    pos1=Get_ObjectPos(object1)
    pos2=Get_ObjectPos(object2)
    diff=Vector_Sub(pos1,pos2)
    return diff[0]*diff[0]+diff[1]*diff[1]+diff[2]*diff[2]

def fallOver(char):
    Trigger_Force(char,0,0,1,Object_GetAttr(char,'minForce'),TF_ABSOLUTE)

def chargeEP(char,cost):
    ep=Object_GetAttr(char,'energyPoints')
    ep=ep-cost
    if ep<0:
       if randint(1,50)<(-ep):
           Object_SetPrimaryState(char,PCSTATE_STUNNED,5,0)
           return 0
    Object_SetAttr(char,'energyPoints',ep)
    return 1

###################### SPECIFIC ATTRIBUTES ################################################

###################### SHAPESHIFTER #######################################################

def initshapeshifter(char,update=0):
    if Mission_GetAttr('shapeReady')==0:
        return
    print 'InitShapeShifter'
    global shapeChangerSets
    global associations
    #if the character has been unpickled, go no further
    if update==1:
        return
    #replace character names with associations to this character
    #check for existing associations
    customName=''
    template=Object_GetTemplate(char)
    match=0
    for assoc in associations:
        if assoc[0]==template:
            match=1
            customName=assoc[1]
    if match==0:
        associate(char,template)
        for assoc in associations:
            if assoc[0]==template:
                match=1
                customName=assoc[1]
    if customName!='':
        print 'custom name identified as %s'%(customName)
        for set in shapeChangerSets:
            if set[0]==customName:
                print 'set Found'
                newSet=[Object_GetTemplate(char)]
                for template in set:
                    if template!=set[0]:
                        newSet.append(template)
                shapeChangerSets.append(newSet)
                print shapeChangerSets
    #identify the number of forms
    set=getShapeSet(Object_GetTemplate(char))
    if (len(set)==0) & (update==0):
        print 'using default set'
        defSet=getShapeSet('default')
        set.append(Object_GetTemplate(char))
        for form in defSet:
            if form!='default':
                set.append(form)
    print 'initshapeshifter'
    print set
    shapeChangerSets.append(set)
    print shapeChangerSets
    i=0
    fromForm=Object_GetTemplate(char)
    for toForm in set:
        i=i+1
        if fromForm!=toForm:
            action=getByTemplate(char,FFX_SHAPESHIFT_CUSTOM,i+2)
            fn='OnForm%d'%(i)
            Mission_CustomAction(action,char,char,fn,5,0)

def getShapeSet(template):
    mySet=[]
    for set in shapeChangerSets:
        for temp in set:
            if temp==template:
                mySet=set
    return mySet

def OnForm1(char,dummy):
    OnChange(char,0)

def OnForm2(char,dummy):
    OnChange(char,1)

def OnForm3(char,dummy):
    OnChange(char,2)

def OnForm4(char,dummy):
    OnChange(char,3)

def OnForm5(char,dummy):
    OnChange(char,4)

def OnForm6(char,dummy):
    OnChange(char,5)

def OnForm7(char,dummy):
    OnChange(char,6)

def OnForm8(char,dummy):
    OnChange(char,7)

def OnChange(char,index):
    if chargeEP(char,FFX_EP_SHAPESHIFT)==0:
        return
    #shapechange identified - lets go!
    set=getShapeSet(Object_GetTemplate(char))
    print set
    print index
    newTemplate=set[index]
    count=Mission_GetAttr('formCount')
    name=getFormName()
    #addHider()
    morph(name,char,newTemplate,1)
    #RegTimer('Change2',0.5,count)

def getFormName():
    count=Mission_GetAttr('formCount')
    dummyName='form_%d'%(count)
    Mission_SetAttr('formCount',count+1)
    return dummyName

#def Change2(event):
#    name='form_%d'%(event.user)
#    awaken(name)
#    delHider()

def makeAttrSafe(char):
    Object_SetAttr(char,'spinning',0)
    Object_SetAttr(char,'lastX',Get_ObjectPos(char)[0])
    Object_SetAttr(char,'lastY',Get_ObjectPos(char)[0])

#turn the character into an object of given template
def oldmorph(name,char,template,proportional=0):
    OnDrop(char,char)
    Mission_SetAttr('maxHealth_%s'%(name),Object_GetAttr(char,'maxHealth'))
    Mission_SetAttr('health_%s'%(name),Object_GetAttr(char,'health'))
    Mission_SetAttr('energyPoints_%s'%(name),Object_GetAttr(char,'energyPoints'))
    Mission_SetAttr('maxEnergyPoints_%s'%(name),Object_GetAttr(char,'maxEnergyPoints'))
    Mission_SetAttr('heroPoints_%s'%(name),Object_GetAttr(char,'heroPoints'))
    Mission_SetAttr('states_%s'%(name),Object_GetSecondaryStates(char))
    Object_Destroy(char)
    RegTimer('morph2',0.05,proportional,template,name)
    pos=Get_ObjectPos(char)
    Mission_SetAttr('x_%s'%(name),pos[0])
    Mission_SetAttr('y_%s'%(name),pos[1])
    Mission_SetAttr('z_%s'%(name),pos[2])
    FFX_ObjectSetAttr(name,'originalIndex',FFX_ObjectGetAttr(char,'originalIndex'))
    print 'morph:transferring index of %d'%(FFX_ObjectGetAttr(char,'originalIndex'))

def oldmorph2(event):
    template=event.object
    name=event.string
    pos=(Mission_GetAttr('x_%s'%(name)),Mission_GetAttr('y_%s'%(name)),Mission_GetAttr('z_%s'%(name)))
    Object_SpawnAt(template,pos,name)
    makeAttrSafe(name)
    if event.user==0:
        Object_SetAttr(name,'health',Mission_GetAttr('health_%s'%(name)))
        Object_SetAttr(name,'maxHealth',Mission_GetAttr('maxHealth_%s'%(name)))
    else:
        Object_SetAttr(name,'health',(Mission_GetAttr('health_%s'%(name))*Object_GetAttr(name,'maxHealth'))/Mission_GetAttr('maxHealth_%s'%(name)))
    Object_SetAttr(name,'energyPoints',Mission_GetAttr('energyPoints_%s'%(name)))
    Object_SetAttr(name,'heroPoints',Mission_GetAttr('heroPoints_%s'%(name)))
    RestoreSStates(name,int(Mission_GetAttr('states_%s'%(name))))
    if (Object_GetClass(name) & js.OC_CONTROLLABLE)!=0:
        Mission_SelectHero(name)
        print 'selecting %s'%(name)
    Object_PlayEffect(name,getByTemplate(name,FFX_SHAPESHIFT_CUSTOM,1))
    awaken(name)
    parseLogFile(event)
    initAttribsForChar(name)

FFX_MIMICDESTROYTEMPTIME=3.5

#turn the character into an object of given template
def morph(name,char,template,proportional=0):
    OnDrop(char,char)
    Mission_SetAttr('maxHealth_%s'%(name),Object_GetAttr(char,'maxHealth'))
    Mission_SetAttr('health_%s'%(name),Object_GetAttr(char,'health'))
    Mission_SetAttr('energyPoints_%s'%(name),Object_GetAttr(char,'energyPoints'))
    Mission_SetAttr('maxEnergyPoints_%s'%(name),Object_GetAttr(char,'maxEnergyPoints'))
    Mission_SetAttr('heroPoints_%s'%(name),Object_GetAttr(char,'heroPoints'))
    Mission_SetAttr('states_%s'%(name),Object_GetSecondaryStates(char))
    ff.RESULTBUBBLE_SCALE=0
    Trigger_Move(char,'lock')
    Object_SetSecondaryState(char,SCSTATE_INVISIBLE,10,0)
    Object_SetSecondaryState(char,SCSTATE_DISPLACE_IMAGE,10,0)
    Object_SetAttr(char,'scannable',0)
    temp=getDummyName()
    cshelperS.spawn(temp,Object_GetTemplate(char),char)
    RegTimer('morphTemp1',0.02,proportional,temp,char)
    RegTimer('morphTemp2',1.3,0,char)
    if (proportional==0) & (Object_GetTemplate(char)==builtinMimics[0]) & (mimicSkin!=''):
        makeMimicSkin(template)
        RegTimer('mimicMorph2',1.7,proportional,template,name)
        RegTimer('morphTemp2',FFX_MIMICDESTROYTEMPTIME,0,temp)
        cshelperS.spawn('mim_%s'%(name),'projectile_standin',char)
    else:
        RegTimer('morph2',1.7,proportional,template,name)
        RegTimer('morphTemp2',1.7,0,temp)
    pos=Get_ObjectPos(char)
    Mission_SetAttr('x_%s'%(name),pos[0])
    Mission_SetAttr('y_%s'%(name),pos[1])
    Mission_SetAttr('z_%s'%(name),pos[2])
    FFX_ObjectSetAttr(name,'originalIndex',FFX_ObjectGetAttr(char,'originalIndex'))

def morphTemp1(event):
    ff.RESULTBUBBLE_SCALE=0.6
    temp=event.object
    char=event.string
    Trigger_Move(temp,'lock')
    if event.user!=0:
        AI_Animate(temp,getByTemplate(char,FFX_SHAPESHIFT_CUSTOM,2))
        Object_PlayEffect(temp,getByTemplate(char,FFX_SHAPESHIFT_CUSTOM,1),'',FX_TRACK_OBJECT_FULL)
    else:
        AI_Animate(temp,getByTemplate(char,FFX_MIMIC_CUSTOM,2))
        Object_PlayEffect(temp,getByTemplate(char,FFX_MIMIC_CUSTOM,1))
    Object_SetAttr(temp,'maxHealth',Object_GetAttr(char,'maxHealth'))
    Object_SetAttr(temp,'health',Object_GetAttr(char,'health'))
    Object_SetAttr(temp,'energyPoints',Object_GetAttr(char,'energyPoints'))

def morphTemp2(event):
    temp=event.object
    Object_Destroy(temp)

def morph2(event):
    template=event.object
    name=event.string
    pos=(Mission_GetAttr('x_%s'%(name)),Mission_GetAttr('y_%s'%(name)),Mission_GetAttr('z_%s'%(name)))
    Object_SpawnAt(template,pos,name)
    makeAttrSafe(name)
    if event.user==0:
        Object_SetAttr(name,'health',Mission_GetAttr('health_%s'%(name)))
        Object_SetAttr(name,'maxHealth',Mission_GetAttr('maxHealth_%s'%(name)))
    else:
        Object_SetAttr(name,'health',(Mission_GetAttr('health_%s'%(name))*Object_GetAttr(name,'maxHealth'))/Mission_GetAttr('maxHealth_%s'%(name)))
    Object_SetAttr(name,'energyPoints',Mission_GetAttr('energyPoints_%s'%(name)))
    Object_SetAttr(name,'heroPoints',Mission_GetAttr('heroPoints_%s'%(name)))
    if (Object_GetClass(name) & js.OC_CONTROLLABLE)!=0:
        Mission_SelectHero(name)
        print 'selecting %s'%(name)
    RegTimer('morph3',0.1,0,name)

def mimicMorph2(event):
    template=event.object
    name=event.string
    pos=(Mission_GetAttr('x_%s'%(name)),Mission_GetAttr('y_%s'%(name)),Mission_GetAttr('z_%s'%(name)))
    Object_Spawn(name,template,'pos_mimic')
    makeAttrSafe(name)
    if event.user==0:
        Object_SetAttr(name,'health',Mission_GetAttr('health_%s'%(name)))
        Object_SetAttr(name,'maxHealth',Mission_GetAttr('maxHealth_%s'%(name)))
    else:
        Object_SetAttr(name,'health',(Mission_GetAttr('health_%s'%(name))*Object_GetAttr(name,'maxHealth'))/Mission_GetAttr('maxHealth_%s'%(name)))
    Object_SetAttr(name,'energyPoints',Mission_GetAttr('energyPoints_%s'%(name)))
    Object_SetAttr(name,'heroPoints',Mission_GetAttr('heroPoints_%s'%(name)))
    if (Object_GetClass(name) & js.OC_CONTROLLABLE)!=0:
        Mission_SelectHero(name)
        print 'selecting %s'%(name)
    RegTimer('morph3',0.2,1,name)

def morph3(event):
    name=event.object
    if event.user==0:
        awaken(name)
    else:
        pos=(Mission_GetAttr('x_%s'%(name)),Mission_GetAttr('y_%s'%(name)),Mission_GetAttr('z_%s'%(name)))
        Trigger_Power(name,'mim_%s'%(name),'ffx_teleport','',0,pos)
    parseLogFile(event)
    update=0
    if name[:5]=='hero_':
        update=1
    initAttribsForChar(name,update)

def getDummyName():
    count=Mission_GetAttr('dummyCount')
    dummyName='mobjdummy_%d'%(count)
    Mission_SetAttr('dummyCount',count+1)
    return dummyName

def awaken(char):
    Trigger_Move(char,'move',Get_ObjectPos(char))

##################### BLOODSUCKER ############################################################

FFX_POWER_CUSTOM=1073741824
FFX_POWER_BUILTIN1=536871040
FFX_POWER_BUILTIN2=536871040
FFX_POWER_BUILTIN3=536871040

def initbloodsucker(char,update=0):
    #if update==0:
    #    RegPowerUse(char,getByTemplate(char,FFX_LIFEDRAIN_CUSTOM,1),'onLifeDrain',1)
    attrName='%s_drain'%(char)
    Global_RegisterAttr(attrName,0)
    for obj in Mission_GetDynamicObjects():
        if ((Object_GetClass(obj) & js.OC_CHARACTER)!=0) & (obj!=char):
            if (Object_GetAttr(obj,attrName)==0) & (Object_GetAttr(obj,'secondTimer')==0):
                Event_RegisterSink(21,'onLifeDrain',obj,char,FFX_POWER_CUSTOM,1.0,1)
                Object_SetAttr(obj,attrName,1)

def OnSuck(target,char):
    Trigger_Power(char,target,getByTemplate(char,FFX_LIFEDRAIN_CUSTOM,1),'')

def onLifeDrain(event):
    if distanceSq(event.object,event.string)>1000:
        return
    print 'onLifeDrain: user=%d'%(event.user)
    Trigger_Damage(event.string,-randint(1,5))

##################### ABSORBER ##############################################################

def initabsorber(char,update=0):
    attrName='%s_absorb'%(char)
    Global_RegisterAttr(attrName,0)
    for obj in Mission_GetDynamicObjects():
        if ((Object_GetClass(obj) & js.OC_CHARACTER)!=0) & (obj!=char):
            if (Object_GetAttr(obj,attrName)==0) & (Object_GetAttr(obj,'secondTimer')==0):
                Event_RegisterSink(21,'onAbsorbEvent',obj,char,FFX_POWER_CUSTOM,1.0,1)
                Object_SetAttr(obj,attrName,1)
    if update==0:
        for template in templates:
            Mission_CustomAction('CUSTOM_ABSORB',char,template[0],'OnAbsorb',FFX_ABSORBRANGE,0)
        Object_SetAttr(char,'templateStrength',Object_GetAttr(char,'strength'))
        Object_SetAttr(char,'templateSpeed',Object_GetAttr(char,'speed'))
        Object_SetAttr(char,'templateEnergy',Object_GetAttr(char,'EPRechargeRate'))

def onAbsorbEvent(event):
    print 'onAbsorbEvent: user=%d'%(event.user)
    OnAbsorb(event.object,event.string)

def OnAbsorb(target,char):
    if (Object_GetClass(target)&OC_CHARACTER)==0:
        Trigger_Power(char,target,getByTemplate(char,FFX_ABSORB_CUSTOM,1),'')
    elif distanceSq(target,char)>1000:
        return
    Object_SetAttr(char,'material',Object_GetAttr(target,'material'))
    absorb(char,target,'maxHealth')
    if (Object_GetClass(target)&OC_CHARACTER)!=0:
        absorb(char,target,'strength')
        absorb(char,target,'speed')
        absorb(char,target,'baseStrength')
        absorb(char,target,'baseSpeed')
        absorb(char,target,'EPRechargeRate')
    print 'absorbed(%s): St=%d Sp=%d M=%d E=%d'%(char,Object_GetAttr(char,'strength'),Object_GetAttr(char,'speed'),Object_GetAttr(char,'material'),Object_GetAttr(char,'EPRechargeRate'))

def absorb(char,target,attr):
    myAttr=Object_GetAttr(char,attr)
    yourAttr=Object_GetAttr(target,attr)
    if yourAttr>myAttr:
        Object_SetAttr(char,attr,yourAttr)
        if attr=='maxHealth':
            hp=Object_GetAttr(char,'health')
            hp=(hp*yourAttr)/myAttr
            Object_SetAttr(char,'health',hp)

def updateAbsorber(char):
    basename=getByTemplate(char,FFX_ABSORB_CUSTOM,2)
    if Object_GetAttr(char,'baseStrength')>Object_GetAttr(char,'templateStrength'):
        Object_PlayEffect(char,'%sstrength'%(basename),'',FX_TRACK_OBJECT_FULL)
        Object_SetAttr(char,'baseStrength',Object_GetAttr(char,'baseStrength')-0.5)
        if Object_GetAttr(char,'strength')<Object_GetAttr(char,'baseStrength'):
            Object_SetAttr(char,'strength',Object_GetAttr(char,'baseStrength'))
    if Object_GetAttr(char,'baseSpeed')>Object_GetAttr(char,'templateSpeed'):
        Object_PlayEffect(char,'%sspeed'%(basename),'',FX_TRACK_OBJECT_FULL)
        Object_SetAttr(char,'baseSpeed',Object_GetAttr(char,'baseSpeed')-0.5)
    if Object_GetAttr(char,'EPRechargeRate')>Object_GetAttr(char,'templateEnergy'):
        Object_PlayEffect(char,'%senergy'%(basename),'',FX_TRACK_OBJECT_FULL)
        Object_SetAttr(char,'EPRechargeRate',Object_GetAttr(char,'EPRechargeRate')-0.5)

######################### MIMIC ##############################################################

def initmimic(char,update=0):
    attrName='%s_mimic'%(char)
    Global_RegisterAttr(attrName,0)
    for obj in Mission_GetDynamicObjects():
        if ((Object_GetClass(obj) & js.OC_CHARACTER)!=0) & (obj!=char):
            if (Object_GetAttr(obj,attrName)==0) & (Object_GetAttr(obj,'secondTimer')==0):
                Mission_CustomAction('CUSTOM_MIMIC',char,obj,'OnMimic',FFX_MIMICRANGE,0)
                Object_SetAttr(obj,attrName,1)

def getMesh(template):
    mesh=''
    for meshPair in ffxMeshes:
        if meshPair[0]==template:
            mesh=meshPair[1]
    return mesh

def makeMimicSkin(template):
    mesh=getMesh(template)
    if mesh=='':
        print 'problem making a mimic skin for %s'%(char)
    try:
        dest='strangers/art/%s'%(mesh)
        os.mkdir(dest)
    except:
        standardAlreadyThere=1
    try:
        dest='strangers/art/%sskins'%(mesh)
        os.mkdir(dest)
    except:
        standardAlreadyThere=1
    try:
        dest='strangers/art/%sskins/standard'%(mesh)
        os.mkdir(dest)
    except:
        standardAlreadyThere=1
    dest='strangers/art/%sskins/standard/mimic.tga'%(mesh)
    print dest
    shutil.copy(mimicSkin,dest)

def OnMimic(target,char):
    print 'OnMimic'
    Trigger_Power(char,target,getByTemplate(char,FFX_MIMIC_CUSTOM,3),'')
    if chargeEP(char,FFX_EP_MIMIC)!=0:
        RegTimer('OnMimicA',1,0,target,char)

def OnMimicA(event):
    target=event.object
    char=event.string
    addHider()
    oldTemplate=Object_GetTemplate(char)
    templateID=getMimicID(oldTemplate)
    if templateID==1000:
        print 'cant resolve template %s : please add to builtinMimics list'%(Object_GetTemplate(char))
        Mission_StatusText('cant resolve template %s : please add to builtinMimics list'%(Object_GetTemplate(char))) 
        return
    count=Mission_GetAttr('formCount')
    dummyName=getFormName()
    template=Object_GetTemplate(target)
    morph(dummyName,char,template,0)
    RegTimer('OnMimic2',2,0,dummyName,oldTemplate)

def OnMimic2(event):
    print 'onMimic2'
    name=event.object
    template=event.string
    templateID=getMimicID(template)
#    index=event.user
    FFX_ObjectSetAttr(name,'mimic',1)
#    Object_SetAttr(name,'heroIndex',index)
    Object_SetAttr(name,'templateID',templateID)
    if (Object_GetClass(name)&OC_CONTROLLABLE)==0:
        AI_MakeIntoHero(name)
        timer=0.05
        if (mimicSkin!='') & (template==builtinMimics[0]):
           timer=2.0
        RegTimer('OnMimicMinion1',timer,Object_GetAttr(name,'health'),name)
    else:
        delHider()
    Mission_CustomAction('CUSTOM_REVERT',name,name,'OnRevert',5,0)
    Mission_SelectHero(name)
    RegCharState(name,PCSTATE_STUNNED,'OnRevertForce')

def OnMimicMinion1(event):
    print 'onMimicMinion1'
    name=event.object
    ff.RESULTBUBBLE_SCALE=0
    Trigger_Damage(event.object,event.user+1)
    RegTimer('OnMimicMinion2',0.05,event.user,name)

def OnMimicMinion2(event):
    ff.RESULTBUBBLE_SCALE=0.6
    print 'onMimicMinion2'
    name=event.object
    js.Object_SetPrimaryState(name, js.PCSTATE_NORMAL, 0)
    RegTimer('OnMimicMinion3',0.05,event.user,name)

def OnMimicMinion3(event):
    print 'onMimicMinion3'
    Object_SetAttr(event.object,'health',event.user)
    delHider()

def getMimicID(template):
    i=0
    for name in builtinMimics:
        if template==name:
            return i
    if cshelperS.getMission()==69:
        numberStr=template[11:]
        print 'getMimicID: template=%s numberStr=%s'%(template,numberStr)
        return -(69+string.atoi(numberStr))
    else:
        numberStr=template[16:]
        print 'getMimicID: template=%s numberStr=%s'%(template,numberStr)
        return -string.atoi(numberStr)

def getRevertTemplate(i):
    if i>=0:
        return builtinMimics[int(i)]
    else:
        if cshelperS.getMission()!=69:
            return 'custom_template_%d'%(-i)
        else:
            return 'squad1_hero%d'%(-(i+69))

def OnRevertForce(event):
    char=event.object
    if Object_Exists(char):
        OnRevert(char,char)

def OnRevert(char,char):
    count=Mission_GetAttr('formCount')
    name=getFormName()
    RegTimer('Change2',1,count)
    Mission_SetAttr('formCount',count+1)
    morph(name,char,getRevertTemplate(Object_GetAttr(char,'templateID')),0)

def RegDeath(object, fn, user = 0):
    js.Event_RegisterSink(EVENT_OBJ_HEALTHCHANGED, fn, object, '', 0, user, 0)

def RegTimer(fn, time, user = 0, thing1='', thing2='', persistent = 0):
    js.Event_RegisterSink(EVENT_TIMER, fn, thing1, thing2, time, user, persistent)

def RegAlmostDead(char, fn, health = 1, user = 0, string = '', persistent = 0):
    Object_SetAttr(char, 'minHealth', health)
    js.Event_RegisterSink(EVENT_OBJ_HEALTHCHANGED, fn, char, string, health+1, user, persistent)

def RegPickup(object, fn, character = '', user = 0):
    js.Event_RegisterSink(EVENT_PICKUP, fn, object, character, 0, 0, 0)

def RegDamage(object, fn, user = 0, persistent = 0):
    js.Event_RegisterSink(EVENT_OBJ_HEALTHCHANGED, fn, object, '', 1000, user, persistent)

def RegShieldDamage(character, fn, user = 0, persistent = 0):
    js.Event_RegisterSink(EVENT_OBJ_SHIELD_DAMAGED, fn, character, '', 1000, user, persistent)

def RegPowerUse(character, power, fn, persistent = 0, user = 0):
    js.Event_RegisterPowerUse(character, power, fn, persistent, user)

############################# BUTTERFINGERS ###########################################

def initbutterfingers(char,update=0):
    print 'initbutterfingers'
    if Mission_GetAttr('pickupReged')!=0:
        return
    Mission_SetAttr('pickupReged',1)
    for i in range(1,1000):
        obj='_impobj_%d'%(i)
        if Object_Exists(obj):
            if (Object_GetAttr(obj,'wieldable')!=0) | (Object_GetAttr(obj,'throwable')!=0):
                RegPickup(obj,'onPickUpBF')

def onPickUpBF(event):
    object=event.object
    char=GetClosestChar2D(object)
    print '%s has picked up %s'%(char,object)
    RegPickup(object,'onPickUpBF')
    if hasAttribute(char,'butterfingers'):
        damage=randint(-5,40)
        if damage>0:
            print 'whoops!:%d pts to %s'%(damage,event.object)
            Trigger_Damage(event.object,damage)
    if hasAttribute(char,'atomtouch'):
        duration=randint(10,20)
        Object_SetGenericState(event.object,OBJSTATE_MOLECULAR_EXCITE,duration,0)

def GetClosestChar2D(obj):
    it=''
    minDist=0
    oPos=Get_ObjectPos(obj)
    for char in Mission_GetDynamicObjects():
        if Object_GetClass(char)&OC_CHARACTER:
            cPos=Get_ObjectPos(char)
            dx=cPos[0]-oPos[0]
            dy=cPos[1]-oPos[1]
            dist=dx*dx+dy*dy
            if (it=='') |  (dist<minDist):
                it=char
                minDist=dist
    return it 


########################### DISPERSED FORM ############################################

def initdispersed(char,update=0):
    if update==0:
        halfHealth=Object_GetAttr(char,'maxHealth')/2
        Object_SetAttr(char,'maxHealth',halfHealth)
        Object_SetAttr(char,'health',halfHealth)
    RegAlmostDead(char,'onDisperse')
    RegDamage(char,'makeGoo')

bodyPart=('Bip01 Head','Bip01 L Forearm','Bip01 R Forearm','Bip01 L Calf','Bip01 R Calf')

def makeGoo(event):
    char=event.object
    Object_PlayEffect(char,getByTemplate(char,FFX_DISPERSE_CUSTOM,4),'',0,0,bodyPart[randint(0,4)])
    RegDamage(char,'makeGoo')

def onDisperse(event):
    char=event.object
    print 'dispersing %s'%(char)
    newHealth=Object_GetAttr(char,'maxHealth')*0.66
    if newHealth>20:
        hero=event.object
        ff.RESULTBUBBLE_SCALE=0
        Object_SetPrimaryState(hero,PCSTATE_PANICKED,100,0)
        Object_SetSecondaryState(hero,SCSTATE_INVISIBLE,100,0)
        Object_SetSecondaryState(hero,SCSTATE_DISPLACE_IMAGE,100,0)
        Object_SetAttr(hero,'speed',0.5)
        RegTimer('keepPanicked',0.1,0,hero)
        RegTimer('UnDisperse',8,0,hero)
        RegTimer('splat',0.1,0,hero)
        pos=Get_ObjectPos(char)
        Object_SetAttr(char,'maxHealth',newHealth)
        Object_SetAttr(char,'health',newHealth)
        RegAlmostDead(char,'onDisperse')
    else:
        Object_SetAttr(char,'minHealth',-1)
        Trigger_Damage(char,10)

def splat(event):
    owner=event.object
    Object_PlayEffect(owner,getByTemplate(owner,FFX_DISPERSE_CUSTOM,2),'',0)
    bubblesOn(event)

def UnDisperse(event):
    hero=event.object
    Object_SetPrimaryState(hero,PCSTATE_PANICKED,0,REMOVE_STATE)
    Object_SetSecondaryState(hero,SCSTATE_INVISIBLE,0,REMOVE_STATE)
    Object_SetSecondaryState(hero,SCSTATE_DISPLACE_IMAGE,0,REMOVE_STATE)
    Object_SetAttr(hero,'speed',Object_GetAttr(hero,'baseSpeed'))
    Object_PlayEffect(hero,getByTemplate(hero,FFX_DISPERSE_CUSTOM,3),'',0)

PICKLE_EXPLODE=1
PICKLE_LIVINGLASER=2
PICKLE_REFORM=3
PICKLE_DETRANSMUTE=4

#def pickleChar(char,timer,pos,action=0):
#    OnDrop(char,char)
#    name='%s%s'%(char,Object_GetTemplate(char))
#    count=Mission_GetAttr('pickleCount')
#    Object_SpawnAt('projectile_standin',pos,name)
#    Object_SetAttr(name,'index',count)
#    Object_SetAttr(name,'length',len(char))
#    Mission_SetAttr('pickleCount',count+1)
#    Object_SetAttr(name,'minHealth',1)
#    Object_SetAttr(name,'maxHealth',Object_GetAttr(char,'maxHealth'))
#    Object_SetAttr(name,'health',Object_GetAttr(char,'health'))
#    Object_SetAttr(name,'energyPoints',Object_GetAttr(char,'energyPoints'))
#    Object_SetAttr(name,'heroPoints',Object_GetAttr(char,'heroPoints'))
#    Object_SetAttr(name,'extraAction',action)
#    Object_SetAttr(name,'heroIndex',Object_GetAttr(char,'heroIndex'))
#    Object_SetAttr(name,'templateID',Object_GetAttr(char,'templateID'))
#    Object_SetAttr(name,'sstate',Object_GetSecondaryStates(char))
#    RegTimer('unPickle',timer,count)
#    if (Object_GetClass(char)&OC_CONTROLLABLE)!=0:
#        addHider()
#    if action!=PICKLE_EXPLODE:
#        Object_Destroy(char)
#    return name
#
#def unPickle(event):
#    for obj in Mission_GetDynamicObjects():
#        if Object_GetTemplate(obj)=='projectile_standin':
#            if (obj[:4]!='mobj') & (obj[:4]!='anti'):
#                if Object_GetAttr(obj,'index')==event.user:
#                    len=int(Object_GetAttr(obj,'length'))
#                    name=obj[:len]
#                    template=obj[len:]
#                    Object_SpawnAt(template,Get_ObjectPos(obj),name)
#                    Object_SetAttr(name,'maxHealth',Object_GetAttr(obj,'maxHealth'))
#                    Object_SetAttr(name,'health',Object_GetAttr(obj,'health'))
#                    Object_SetAttr(name,'energyPoints',Object_GetAttr(obj,'energyPoints'))
#                    Object_SetAttr(name,'heroPoints',Object_GetAttr(obj,'heroPoints'))
#                    Object_SetAttr(name,'heroIndex',Object_GetAttr(obj,'heroIndex'))
#                    Object_SetAttr(name,'templateID',Object_GetAttr(obj,'templateID'))
#                    Object_SetAttr(name,'secondTimer',1)
#                    RestoreSStates(name,int(Object_GetAttr(obj,'sstate')))
#                    makeAttrSafe(name)
#                    action=Object_GetAttr(obj,'extraAction')
#                    #need to restore stuff like dispersed here
#                    reInit(name,1)
#                    if action==PICKLE_EXPLODE:
#                        Object_PlayEffect(name,getByTemplate(name,FFX_EXPLODE_CUSTOM,3),'',0)
#                    elif action==PICKLE_LIVINGLASER:
#                        Mission_SelectHero(name)
#                        Object_PlayEffect(name,getByTemplate(name,FFX_LIVINGLASER_CUSTOM,3),'',0)
#                    elif action==PICKLE_REFORM:
#                        Object_SetPrimaryState(name,PCSTATE_STUNNED,1,0)
#                        Object_PlayEffect(name,getByTemplate(name,FFX_DISPERSE_CUSTOM,3),'',0)
#                    elif action==PICKLE_DETRANSMUTE:
#                        Object_SetPrimaryState(name,PCSTATE_STUNNED,1,0)
#                    Object_Destroy(obj)
#                    awaken(name)
#                    if (Object_GetClass(name)&OC_CONTROLLABLE)!=0:
#                        delHider()
#
def RestoreSStates(char,states):
    for state in (SCSTATE_IRRADIATED,SCSTATE_BLIND,SCSTATE_HEXED,SCSTATE_ACID_BURNT,SCSTATE_POWER_NULLIFICATION,SCSTATE_ENERGIZED,SCSTATE_EMPATHY,SCSTATE_AREA_ENERGIZED):
        if (states&state!=0):
            Object_SetSecondaryState(char,state,5,0)
#
#def DoPendingPickles(event):
#    #search the 'pickles' list for ones whose matching hero aint destroyed, and destroy the hero
#    for obj in Mission_GetDynamicObjects():
#        if Object_GetTemplate(obj)=='projectile_standin':
#            if (obj[:4]!='mobj') & (obj[:4]!='anti'):
#                len=int(Object_GetAttr(obj,'length'))
#                name=obj[:len]
#                if Object_Exists(name):
#                    Object_Destroy(name)
#
#def unPickleAll():
#    for obj in Mission_GetDynamicObjects():
#        if Object_GetTemplate(obj)=='projectile_standin':
#            if (obj[:4]!='mobj') & (obj[:4]!='anti'):
#                RegTimer('unPickle',0.1,Object_GetAttr(obj,'index'))

########################### DEPRESSED #################################################

def initdepressed(char,update=0):
    nothing=0

########################### HOLLOW BONES ##############################################

def inithollowbones(char,update=0):
    Object_SetAttr(char,'mass',Object_GetAttr(char,'mass')*0.66)
    Object_SetAttr(char,'minForce',Object_GetAttr(char,'minForce')*0.4)

########################### LIVING LASER ###############################################

def initlivinglaser(char,update=0):
    attrName='%s_laser'%(char)
    Global_RegisterAttr(attrName,0)
    cmdName=getByTemplate(char,FFX_LIVINGLASER_CUSTOM,4)
    if update==0:
        for template in templates:
            Mission_CustomAction(cmdName,char,template[0],'OnLaserTo',50,0)
    for obj in Mission_GetDynamicObjects():
        if ((Object_GetClass(obj) & js.OC_CHARACTER)!=0) & (obj!=char):
            if (Object_GetAttr(obj,attrName)==0) & (Object_GetAttr(obj,'secondTimer')==0):
                Mission_CustomAction(cmdName,char,obj,'OnLaserTo',50,0)
                Object_SetAttr(obj,attrName,1)

def OnLaserTo(target,hero):
    if chargeEP(hero,FFX_EP_LIVINGLASER)==0:
        return
    Object_PlayEffect(hero,getByTemplate(hero,FFX_LIVINGLASER_CUSTOM,2),'',0)
    Trigger_Power(hero,target,getByTemplate(hero,FFX_LIVINGLASER_CUSTOM,1),'OnLaserHit')

def OnLaserHit(event):
    target=event.object
    hero=event.string
    if Object_GetPrimaryState(hero)!=PCSTATE_NORMAL:
        return
    ff.RESULTBUBBLE_SCALE=0
    pos=Get_ObjectPos(hero)
    Object_SetSecondaryState(hero,SCSTATE_INVISIBLE,100,0)
    Object_SetSecondaryState(hero,SCSTATE_DISPLACE_IMAGE,100,0)
    Object_SetAttr(hero,'speed',9)
    RegTimer('UnLaser',1,0,hero)
    RegTimer('bubblesOn',0.1)
    posT=Get_ObjectPos(target)
    posH=Get_ObjectPos(hero)
    dx=posH[0]-posT[0]
    dy=posH[1]-posT[1]
    len=1.0+sqrt(dx*dx+dy*dy)
    len=20.0/len
    Trigger_Power(hero,target,'ffx_sprint','',0,(posT[0]+dx*len,posT[1]+dy*len,posT[2]))

def UnLaser(event):
    hero=event.object
    Object_PlayEffect(hero,getByTemplate(hero,FFX_LIVINGLASER_CUSTOM,3),'',0)
    Object_SetSecondaryState(hero,SCSTATE_INVISIBLE,0,REMOVE_STATE)
    Object_SetSecondaryState(hero,SCSTATE_DISPLACE_IMAGE,0,REMOVE_STATE)
    Object_SetAttr(hero,'speed',Object_GetAttr(hero,'baseSpeed'))
    Object_PlayEffect(hero,getByTemplate(hero,FFX_EXPLODE_CUSTOM,3),'',0)

def laserFlash(event):
    flashName='mobjdummy_%d'%(event.user)
    Object_PlayEffect(flashName,getByTemplate(flashName,FFX_LIVINGLASER_CUSTOM,2),'destroyTemp',0)

def destroyTemp(event):
    print 'destroyTemp:%s'%(event.object)
    Object_Destroy(event.object)

########################### EXPLOSIVE ###############################################

def initexplosive(char,update=0):
    if update==0:
        Mission_CustomAction('CUSTOM_EXPLODE',char,char,'OnExplode',50,0)

def OnExplode(target,hero):
    if chargeEP(hero,FFX_EP_EXPLODE)==0:
        return
    AI_Animate(hero,getByTemplate(hero,FFX_EXPLODE_CUSTOM,1),'OnExplode2')

def OnExplode2(event):
    hero=event.object
    ff.RESULTBUBBLE_SCALE=0
    Trigger_Explosion(hero,getByTemplate(hero,FFX_EXPLODE_CUSTOM,2))
    Object_SetPrimaryState(hero,PCSTATE_PANICKED,100,0)
    Object_SetSecondaryState(hero,SCSTATE_INVISIBLE,100,0)
    Object_SetSecondaryState(hero,SCSTATE_DISPLACE_IMAGE,100,0)
    RegTimer('keepPanicked',0.1,0,hero)
    Object_SetAttr(hero,'speed',0.5)
    RegTimer('UnExplode',8,0,hero)
    RegTimer('bubblesOn',0.1)

def keepPanicked(event):
    char=event.object
    if Object_Exists(char):
        if (Object_GetSecondaryState(char)&SCSTATE_INVISIBLE)!=0:
            if Object_GetPrimaryState(char)!=PCSTATE_PANICKED:
                Object_SetPrimaryState(char,PCSTATE_PANICKED,100,0)
        RegTimer('keepPanicked',0.1,0,char)

def bubblesOff(event):
    ff.RESULTBUBBLE_SCALE=0

def bubblesOn(event):
    ff.RESULTBUBBLE_SCALE=0.6

def UnExplode(event):
    hero=event.object
    Object_SetPrimaryState(hero,PCSTATE_PANICKED,0,REMOVE_STATE)
    Object_SetSecondaryState(hero,SCSTATE_INVISIBLE,0,REMOVE_STATE)
    Object_SetSecondaryState(hero,SCSTATE_DISPLACE_IMAGE,0,REMOVE_STATE)
    Object_SetAttr(hero,'speed',Object_GetAttr(hero,'baseSpeed'))
    Object_PlayEffect(hero,getByTemplate(hero,FFX_EXPLODE_CUSTOM,3),'',0)
    
#def OnExplode3(event):
#    DoPendingPickles(event)

############################# ATOMIC TOUCH ###########################################

def initatomtouch(char,update=0):
    print 'initatomtouch'
    initbutterfingers(char)

########################## BATTERY POWERED ############################################

def initbattery(char,update=0):
    batterySize=200*Object_GetAttr(char,'EPRechargeRate')
    oldMax=Object_GetAttr(char,'energyPoints')
    Object_SetAttr(char,'maxEnergyPoints',batterySize)
    #if update==0:
    Object_SetAttr(char,'energyPoints',Object_GetAttr(char,'energyPoints')/oldMax*batterySize)
    Object_SetAttr(char,'EPRechargeRate',0)

######################### PRESENCE #####################################################

def initpresence(char,update=0):
    doNothing=0

def updatePresence(obj):
    if Object_IsAlive(obj)==0:
        return
    for minion in Mission_GetDynamicObjects():
        if ((Object_GetClass(minion)&OC_MINION)!=0):
            if randint(1,100)<=FFX_PRESENCE_CHANCE:
                if distanceSq(minion,obj)<(300*300):
                    Object_SetPrimaryState(minion,PCSTATE_PANICKED,30,0)

######################### MINDLESS #####################################################

def initmindless(char,update=0):
    RegCharState(char,PCSTATE_BLANK,'clearMindBlank')
    RegCharState(char,PCSTATE_HYPNOTISED,'clearMindHyp')
    RegCharState(char,PCSTATE_ENRAGED,'clearMindRage')
    RegCharState(char,PCSTATE_PANICKED,'clearMindFear')

def clearMindBlank(event):
    char=event.object
    Object_SetPrimaryState(char,PCSTATE_BLANK,0,REMOVE_STATE)
    RegCharState(char,PCSTATE_BLANK,'clearMindBlank')

def clearMindHyp(event):
    char=event.object
    Object_SetPrimaryState(char,PCSTATE_HYPNOTISED,0,REMOVE_STATE)
    RegCharState(char,PCSTATE_HYPNOTISED,'clearMindHyp')

def clearMindRage(event):
    char=event.object
    Object_SetPrimaryState(char,PCSTATE_NORMAL,1,0)
    RegCharState(char,PCSTATE_ENRAGED,'clearMindRage')

def clearMindFear(event):
    char=event.object
    Object_SetPrimaryState(char,PCSTATE_PANICKED,0,REMOVE_STATE)
    RegCharState(char,PCSTATE_PANICKED,'clearMindFear')

def RegCharState(character, state, fn, persistent = 0):
    js.Event_RegisterSink(EVENT_CHARACTER_STATE_CHANGED, fn, character, '', 0, state, persistent)

######################## FEARLESS ##########################################

def initfearless(char,update=0):
    RegCharState(char,PCSTATE_PANICKED,'clearMindFear')

######################## DISPASSIONATE ######################################

def initdispassionate(char,update=0):
    RegCharState(char,PCSTATE_ENRAGED,'clearMindRage')

######################## IRON JAW ##########################################

def initironjaw(char,update=0):
    RegCharState(char,PCSTATE_STUNNED,'clearStun')

def clearStun(event):
    char=event.object
    Object_SetPrimaryState(char,PCSTATE_STUNNED,0,REMOVE_STATE)
    RegCharState(char,PCSTATE_STUNNED,'clearStun')

######################### JINX #############################################

def initjinx(char,update=0):
    doNothing=0

def updateJinx(obj):
    if Object_IsAlive(obj)==0:
        return
    for char in Mission_GetDynamicObjects():
        if ((Object_GetClass(char)&OC_CHARACTER)!=0) & (char!=obj):
            if distanceSq(char,obj)<(300*300):
                if randint(1,100)<=FFX_JINX_CHANCE_JAM:
                    Object_SetSecondaryState(char,SCSTATE_POWER_NULLIFICATION,15,0)
                if randint(1,100)<=FFX_JINX_CHANCE_SLIP:
                    fallOver(char)

########################## PEACE FIELD #####################################

def initpeacefield(char,update=0):
    if update==0:
        Mission_CustomAction('CUSTOM_PEACEFIELD',char,char,'OnPeaceField',5,0)

def OnPeaceField(char,dummy):
    if chargeEP(char,FFX_EP_PEACEFIELD)==0:
        return
    AI_Animate(char,getByTemplate(char,FFX_PEACE_CUSTOM,1),'')
    RegTimer('PeaceField1',0.1,20,char)
    handle=Object_PlayEffect(char,getByTemplate(char,FFX_PEACE_CUSTOM,2),'',FX_TRACK_OBJECT_FULL|FX_LOOP)
    Object_SetAttr(char,'peaceHandle',handle)

def PeaceField1(event):
    peaceKeeper=event.object
    if Object_Exists(peaceKeeper)==0:
        return
    if Object_IsAlive(peaceKeeper)==0:
        return
    if (event.user!=0):
        RegTimer('PeaceField1',0.5,event.user-1,peaceKeeper)
    else:
        Object_StopEffect(peaceKeeper,Object_GetAttr(peaceKeeper,'peaceHandle'))
    for obj in Mission_GetDynamicObjects():
        if Object_GetTemplate(obj)=='projectile_standin':
            if obj[:5]=='mobj_':
                if distanceSq(obj,peaceKeeper)<300*300:
                    name='anti%s'%(obj)
                    Object_SpawnAt('projectile_standin',Get_ObjectPos(obj),name)
                    Object_Destroy(obj)
    RegTimer('PeaceField2',0.1,0,peaceKeeper)

def PeaceField2(event):
    peaceKeeper=event.object
    for obj in Mission_GetDynamicObjects():
        if Object_GetTemplate(obj)=='projectile_standin':
            if obj[:9]=='antimobj_':
                Object_PlayEffect(obj,getByTemplate(peaceKeeper,FFX_PEACE_CUSTOM,3),'destroyTemp',0)


####################### GASEOUS FORM ###########################################

def initgaseousform(char,update=0):
    if update==0:
        Mission_CustomAction('CUSTOM_GASEOUSFORM',char,char,'OnGaseousForm',5,0)

def OnGaseousForm(char,dummy):
    if chargeEP(char,FFX_EP_GAS)==0:
        return
    Object_SetSecondaryState(char,SCSTATE_DISPLACE_IMAGE,20,0)
    Object_SetSecondaryState(char,SCSTATE_INVISIBLE,20,0)
    Object_SetAttr(char,'ffxState',FFX_STATE_GAS)

def updateGaseousForm(obj):
    if ((Object_GetSecondaryStates(obj)&SCSTATE_DISPLACE_IMAGE)!=0) & (Object_GetAttr(obj,'ffxState')==FFX_STATE_GAS):
       Trigger_Explosion(obj,'ffx_gaseousform')
    elif (Object_GetAttr(obj,'ffxState')==FFX_STATE_GAS):
       Object_SetAttr(obj,'ffxState',0)

####################### SANDSTORM ###########################################

def initsandstorm(char,update=0):
    if update==0:
        Mission_CustomAction('CUSTOM_SANDSTORM',char,char,'OnSandStorm',5,0)
    Object_SetAttr(char,'fxHandle',-1)

def OnSandStorm(char,dummy):
    if chargeEP(char,FFX_EP_SAND)==0:
        return
    Object_SetSecondaryState(char,SCSTATE_DISPLACE_IMAGE,10,0)
    Object_SetSecondaryState(char,SCSTATE_INVISIBLE,10,0)
    Object_SetAttr(char,'ffxState',FFX_STATE_SAND)
    pos=Get_ObjectPos(char)
    Object_SetAttr(char,'lastX',pos[0])
    Object_SetAttr(char,'lastY',pos[1])

def updateSandstorm(obj):
    if ((Object_GetSecondaryStates(obj)&SCSTATE_DISPLACE_IMAGE)!=0) & (Object_GetAttr(obj,'ffxState')==FFX_STATE_SAND):
       Trigger_Explosion(obj,'ffx_sandstorm')
       if Object_GetAttr(obj,'fxHandle')==-1:
           handle=Object_PlayEffect(obj,'effect_sandstorm','',FX_LOOP|FX_TRACK_OBJECT_FULL)
           Object_SetAttr(obj,'fxHandle',handle)
       pos=Get_ObjectPos(obj)
       if (pos[0]==Object_GetAttr(obj,'lastX')) & (pos[1]==Object_GetAttr(obj,'lastY')):
           Trigger_Move(obj,'move',(pos[0]+randint(-50,50),pos[1]+randint(-50,50),pos[2]))
       Object_SetAttr(obj,'lastX',pos[0])
       Object_SetAttr(obj,'lastY',pos[1])
    elif (Object_GetAttr(obj,'ffxState')==FFX_STATE_SAND):
       Object_SetAttr(obj,'ffxState',0)
       if Object_GetAttr(obj,'fxHandle')!=-1:
           Object_StopEffect(obj,int(Object_GetAttr(obj,'fxHandle')))
           Object_SetAttr(obj,'fxHandle',-1)

####################### HUMAN TOP ###########################################

def initspinner(char,update=0):
    if update==0:
        Mission_CustomAction('CUSTOM_SPIN',char,char,'OnSpin',5,0)
    Object_SetAttr(char,'fxHandle',-1)
    Object_SetAttr(char,'spinning',0)

def OnSpin(char,dummy):
    if chargeEP(char,FFX_EP_SPIN)==0:
        return
    Object_SetAttr(char,'spinning',10)
    Object_SetAttr(char,'ffxState',FFX_STATE_SPIN)
    Object_SetAttr(char,'speed',9)
    pos=Get_ObjectPos(char)
    Object_SetAttr(char,'lastX',pos[0])
    Object_SetAttr(char,'lastY',pos[1])

def updateSpinner(obj):
    stop=0
    if Object_GetAttr(obj,'spinning')>0:
        Object_SetAttr(obj,'spinning',Object_GetAttr(obj,'spinning')-1)
        pState=Object_GetPrimaryState(obj)
        #stop spinning if we trigger another power or get physically stopped
        if (pState==PCSTATE_FROZEN) | (pState==PCSTATE_STATIC) | (pState==PCSTATE_STUNNED) | (Object_GetAttr(obj,'ffxState')!=FFX_STATE_SPIN) | (Object_GetAttr(obj,'spinning')<=0):
            stop=1
        else:
            if Object_GetAttr(obj,'fxHandle')==-1:
                handle=Object_PlayEffect(obj,'effect_sandstorm','',FX_LOOP|FX_TRACK_OBJECT_FULL)
                Object_SetAttr(obj,'fxHandle',handle)
            Trigger_Explosion(obj,'ffx_superrotate')
            stop=0
            pos=Get_ObjectPos(obj)
            if (pos[0]==Object_GetAttr(obj,'lastX')) & (pos[1]==Object_GetAttr(obj,'lastY')):
                Trigger_Move(obj,'move',(pos[0]+randint(-50,50),pos[1]+randint(-50,50),pos[2]))
            Object_SetAttr(obj,'lastX',pos[0])
            Object_SetAttr(obj,'lastY',pos[1])
        if stop!=0:
            Object_SetAttr(obj,'spinning',0)
            if Object_GetAttr(obj,'ffxState')==FFX_STATE_SPIN:
                Object_SetAttr(obj,'ffxState',0)
            if Object_GetAttr(obj,'fxHandle')!=-1:
                Object_SetAttr(obj,'speed',Object_GetAttr(obj,'baseSpeed'))
                Object_StopEffect(obj,int(Object_GetAttr(obj,'fxHandle')))
                Object_SetAttr(obj,'fxHandle',-1)

####################### ICE CLOUD ###########################################

def initicecloud(char,update=0):
    if update==0:
        Mission_CustomAction('CUSTOM_ICECLOUD',char,char,'OnIceCloud',5,0)

def OnIceCloud(char,dummy):
    if chargeEP(char,FFX_EP_ICE)==0:
        return
    Object_SetSecondaryState(char,SCSTATE_DISPLACE_IMAGE,20,0)
    Object_SetAttr(char,'ffxState',FFX_STATE_ICE)

def updateIceCloud(obj):
    if ((Object_GetSecondaryStates(obj)&SCSTATE_DISPLACE_IMAGE)!=0) & (Object_GetAttr(obj,'ffxState')==FFX_STATE_ICE):
       Trigger_Explosion(obj,'ffx_icecloud')
    elif (Object_GetAttr(obj,'ffxState')==FFX_STATE_ICE):
       Object_SetAttr(obj,'ffxState',0)

##################### VERY UNSTABLE MOLECULES ###############################

def initvunstablemols(char,update=0):
    doNothing=0
 
####################### BIOHAZARD ###########################################

def initbiohazard(char,update=0):
    doNothing=0

def initfrostbite(char,update=0):
    Object_PlayEffect(char,'effect_ffx_icesparks','',FX_LOOP|FX_TRACK_OBJECT_FULL,0,'Bip01 Spine')

def initoverheated(char,update=0):
    Object_PlayEffect(char,'effect_ffx_flames','',FX_LOOP|FX_TRACK_OBJECT_FULL,0,'Bip01 Spine')

def initshortcirc(char,update=0):
    Object_PlayEffect(char,'effect_ffx_electricsparks','',FX_LOOP|FX_TRACK_OBJECT_POS,0,'Bip01 Spine')

####################### NUCLEAR STOCKPILE ###################################

def initstockpile(char,update=0):
    RegDamage(char,'OnStockPileHit')
    Object_SetAttr(char,'stockpile',0)
    Object_SetAttr(char,'lastHealthNS',Object_GetAttr(char,'health'))
    Object_SetAttr(char,'pileHandle1',-1)
    Object_SetAttr(char,'pileHandle2',-1)

def OnStockPileHit(event):
    char=event.object
    RegDamage(char,'OnStockPileHit')
    pile=Object_GetAttr(char,'stockpile')
    damage=Object_GetAttr(char,'lastHealthNS')-Object_GetAttr(char,'health')
    if damage<1:
       damage=1    
    Object_SetAttr(char,'lastHealthNS',Object_GetAttr(char,'health'))
    pile=pile+damage
    if randint(1,100)<pile:
        #blow up!
        intensity=1+(pile/20)
        pile=0
        if Object_GetAttr(char,'pileHandle1')!=-1:
            Object_StopEffect(char,Object_GetAttr(char,'pileHandle1'))
            Object_SetAttr(char,'pileHandle1',-1)  
        if Object_GetAttr(char,'pileHandle2')!=-1:
            Object_StopEffect(char,Object_GetAttr(char,'pileHandle2'))
            Object_SetAttr(char,'pileHandle2',-1)      
        Trigger_Explosion(char,getByTemplate(char,FFX_STOCKPILE_CUSTOM,2),intensity)
    else:
        if (pile>20) & (Object_GetAttr(char,'pileHandle1')==-1):       
            handle=Object_PlayEffect(char,getByTemplate(char,FFX_STOCKPILE_CUSTOM,1),'',FX_TRACK_OBJECT_FULL|FX_LOOP,0,'')
            Object_SetAttr(char,'pileHandle1',handle)
        if (pile>50) & (Object_GetAttr(char,'pileHandle2')==-1):       
            handle=Object_PlayEffect(char,getByTemplate(char,FFX_STOCKPILE_CUSTOM,1),'',FX_TRACK_OBJECT_FULL|FX_LOOP,0,'Bip01 Head')
            Object_SetAttr(char,'pileHandle2',handle)
    Object_SetAttr(char,'stockpile',pile) 
        
################## blast shield ##############################

def initblastshield(char,update=0):
    RegShieldDamage(char,'OnBlastShield',1)

def OnBlastShield(event):
    RegShieldDamage(event.object,'OnBlastShield',1)
    Trigger_Explosion(event.object,'generic area energize')

################### acid blood #################################

def initacidblood(char,update=0):
    Object_SetAttr(char,'lastHealthAB',Object_GetAttr(char,'health'))
    RegDamage(char,'OnAcidBlood')

def OnAcidBlood(event):
    char=event.object
    damage=Object_GetAttr(char,'lastHealthAB')-Object_GetAttr(char,'health')
    if damage>0:
        if randint(1,25)<damage:
            Trigger_Explosion(char,'ffx_acidblood')
    Object_SetAttr(char,'lastHealthAB',Object_GetAttr(char,'health'))
    RegDamage(char,'OnAcidBlood')
	
################### glass jaw #################################

def initglassjaw(char,update=0):
    Object_SetAttr(char,'lastHealthGJ',Object_GetAttr(char,'health'))
    RegDamage(char,'OnGlassJaw')

def OnGlassJaw(event):
    char=event.object
    damage=Object_GetAttr(char,'lastHealthGJ')-Object_GetAttr(char,'health')
    if damage>0:
        if randint(1,25)<damage:
            Object_SetPrimaryState(char,PCSTATE_STUNNED,5,0)
    Object_SetAttr(char,'lastHealthGJ',Object_GetAttr(char,'health'))
    RegDamage(char,'OnGlassJaw')

################### psychic conduit #############################

def initconduit(char,update=0):
    Object_SetAttr(char,'lastHealthPC',Object_GetAttr(char,'health'))
    RegDamage(char,'OnConduit')

def OnConduit(event):
    char=event.object
    damage=Object_GetAttr(char,'lastHealthPC')-Object_GetAttr(char,'health')
    if damage>0:
        if randint(1,2)==1:
            Trigger_Explosion(char,'ffx_conduit1')
        if randint(1,2)==1:
            Trigger_Explosion(char,'ffx_conduit2')
    Object_SetAttr(char,'lastHealthPC',Object_GetAttr(char,'health'))
    RegDamage(char,'OnConduit')

#################### spiny ######################################

def initspiny(char,update=0):
    Object_SetAttr(char,'lastHealthSP',Object_GetAttr(char,'health'))
    RegDamage(char,'OnSpiny')

def OnSpiny(event):
    char=event.object
    damage=Object_GetAttr(char,'lastHealthSP')-Object_GetAttr(char,'health')
    if damage>0:
        Trigger_Explosion(char,'ffx_spiny',-1)
    Object_SetAttr(char,'lastHealthSP',Object_GetAttr(char,'health'))
    RegDamage(char,'OnSpiny')

################# sensitive ######################################

def initsensitive(char,update=0):
    doNothing=0

################ telepathy ########################################

def inittelepathy(char,update=0):
    Mission_SetAttr('tpArrow',0)
    Mission_CustomAction('CUSTOM_SCAN',char,char,'OnScan',5,0)

def OnScan(char,dummy):
    if chargeEP(char,FFX_EP_SCAN)==0:
        return
    AI_Animate(char,getByTemplate(char,FFX_SCAN_CUSTOM,1))
    Object_PlayEffect(char,getByTemplate(char,FFX_SCAN_CUSTOM,2),'',0,0,'Bip01 Head')
    for obj in Mission_GetDynamicObjects():
        if (Object_GetClass(obj)&(OC_VILLIAN|OC_MINION))!=0:
            name='tparrow_%d'%(Mission_GetAttr('tpArrow'))
            Mission_SetAttr('tpArrow',Mission_GetAttr('tpArrow')+1)
            Mission_AddArrowObject(name,obj,js.ARROW_YELLOW,1)
    RegTimer('RemoveScanArrows',15)

def RemoveScanArrows(event):
    count=0
    while count<Mission_GetAttr('tpArrow'):
        name='tparrow_%d'%(count)
        Mission_RemoveArrow(name)
        count=count+1
    Mission_SetAttr('tpArrow',0) 
	
################# symbiote #######################################

def initsymbiote(char,update=0):
    doNothing=0   

################# interference #######################################

def initparasite(char,update=0):
    doNothing=0   

############### temporary form ########################################

def inittempform(char,update=0):
    initshapeshifter(char,update)

############### pheremones ############################################

def initpheremones(char,update=0):
    targetGender=getByTemplate(char,FFX_PHEREMONE_CUSTOM,2)
    setGender(char)
    myGender=int(Object_GetAttr(char,'gender'))
    if targetGender==FFX_GENDER_UNKNOWN:
        if (myGender&31)==FFX_GENDER_FEMALE:
            targetGender=FFX_GENDER_MALE
        else:
            targetGender=FFX_GENDER_FEMALE
    if (myGender&FFX_GENDER_EVIL)==0:
        targetGender=targetGender+FFX_GENDER_EVIL
    FFX_ObjectSetAttr(char,'targetGender',targetGender)

def updatePheremones(obj):
    print 'initpheremones:%s is a %s(%d)'%(obj,Object_GetTemplate(obj),hasAttribute(obj,'pheremones'))
    fx=getByTemplate(obj,FFX_PHEREMONE_CUSTOM,1)
    if fx!='':
        Object_PlayEffect(obj,fx)
    for char in Mission_GetDynamicObjects():
        if ((Object_GetClass(char)&OC_CHARACTER)!=0):
            if Object_GetAttr(char,'gender')==FFX_ObjectGetAttr(obj,'targetGender'):
                print 'checking %s for pheremone control'%(char)
                if randint(1,100)<=FFX_PHEREMONE_CHANCE:
                    if distanceSq(char,obj)<(250*250):
                        Object_SetPrimaryState(char,PCSTATE_HYPNOTISED,30,0)

########### TELEKINETIC, SUPERTELEKINETIC AND MAGNETIC ###############################################

FFX_MATERIAL_ANY=-1
FFX_MATERIAL_METAL=1
FFX_MATERIAL_STONE=2

def inittelekinetic(char,update=0):
    inittk(char,250,FFX_MATERIAL_ANY,update)

def initsupertk(char,update=0):
    inittk(char,5000,FFX_MATERIAL_ANY,update)

def initmagnetic(char,update=0):
    inittk(char,5000,FFX_MATERIAL_METAL,update)

def initearthcontrol(char,update=0):
    inittk(char,5000,FFX_MATERIAL_STONE,update)

#generic telekinesis

def isTKable(obj):
    return (Object_GetAttr(obj,'throwable')!=0) | (Object_GetAttr(obj,'wieldable')!=0)

#old version
def inittk(char,maxMass,materialType,update=0):
    print 'inittk: update=%d'%(update)
    attr='%s_tk'%(char)
    Object_SetAttr(char,'tkType',materialType)
    Object_SetAttr(char,'maxMass',maxMass)
    if update==0:
        FFX_ObjectSetAttr(char,'tiggotCount',1)
        FFX_ObjectSetAttr(char,'tkCom',0)
    if (Object_GetClass(char)&OC_CONTROLLABLE)==0:
        return
    Global_RegisterAttr(attr,0)
    if update==0:
        for temp in templates:
            mass=temp[1]
            material=temp[2]
            if (mass<=maxMass) & ((material==materialType)|(materialType==-1)):
                Mission_CustomAction('CUSTOM_LIFT',char,temp[0],'OnLift',30,0)
    for obj in Mission_GetDynamicObjects():
        if (Object_GetAttr(obj,attr)==0) & (Object_GetAttr(obj,'secondTimer')==0):
            Object_SetAttr(obj,attr,1)
            if obj!=char:
                if (isTKable(obj)) & ((Object_GetClass(obj)&OC_CHARACTER)==0):
                     temp=Object_GetTemplate(obj)
                     match=0
                     for template in templates:
                         if temp==template[0]:
                             match=1
                     if match==0:
                         mass=Object_GetAttr(obj,'mass')           		 
                         material=Object_GetAttr(obj,'material')
                         if (mass<=maxMass) & ((material==materialType)|(materialType==-1)):
                             Mission_CustomAction('CUSTOM_LIFT',char,obj,'OnLift',30,0)
                #add attract and repel commands if magnetic and metal object
                if (materialType==FFX_MATERIAL_METAL) & (Object_GetAttr(obj,'material')==materialType):
                    Mission_CustomAction('CUSTOM_ATTRACT',char,obj,'OnAttract',30,0)
                    Mission_CustomAction('CUSTOM_REPEL',char,obj,'OnRepel',30,0)
                #add wall and spike to earth control powers
                if materialType==FFX_MATERIAL_STONE:
                    Mission_CustomAction('CUSTOM_SPIKE',char,obj,'OnSpike',30,0)
                    Mission_CustomAction('CUSTOM_WALL',char,obj,'OnWall',30,0)
                if FFX_ObjectGetAttr(char,'tkCom')!=0:
                    if ( (Object_GetClass(obj)&OC_CHARACTER)!=0):
                        Mission_CustomAction('CUSTOM_THROWTO',char,obj,'OnThrowTo',30,0)
                        Mission_CustomAction('CUSTOM_MOVETO',char,obj,'OnMoveTo',30,0)
                        

#add throw to and move to commands for TK
def AddTKCommands(char):
    if (Object_GetClass(char)&OC_CONTROLLABLE)==0:
        return
    if FFX_ObjectGetAttr(char,'tkCom')!=0:
        return
    FFX_ObjectSetAttr(char,'tkCom',1)
    Mission_CustomAction('CUSTOM_DROP',char,char,'OnDrop',5,0)
    for temp in templates:
        Mission_CustomAction('CUSTOM_THROWTO',char,temp[0],'OnThrowTo',30,0)
        Mission_CustomAction('CUSTOM_MOVETO',char,temp[0],'OnMoveTo',30,0)
    for obj in Mission_GetDynamicObjects():
        temp=Object_GetTemplate(obj)
        match=0
        for template in templates:
            if temp==template[0]:
                match=1
        if match==0:
            Mission_CustomAction('CUSTOM_THROWTO',char,obj,'OnThrowTo',30,0)
            Mission_CustomAction('CUSTOM_MOVETO',char,obj,'OnMoveTo',30,0)

def RemoveTKCommands(char):
    if (Object_GetClass(char)&OC_CONTROLLABLE)==0:
        return
    FFX_ObjectSetAttr(char,'tkCom',0)
    Mission_RemoveCustomAction('CUSTOM_DROP',char,char)
    for temp in templates:
        Mission_RemoveCustomAction('CUSTOM_THROWTO',char,temp[0])
        Mission_RemoveCustomAction('CUSTOM_MOVETO',char,temp[0])
    for obj in Mission_GetDynamicObjects():
        temp=Object_GetTemplate(obj)
        match=0
        for template in templates:
            if temp==template[0]:
                match=1
        if match==0:
            Mission_RemoveCustomAction('CUSTOM_THROWTO',char,obj)
            Mission_RemoveCustomAction('CUSTOM_MOVETO',char,obj)

def OnTKPower(char,target,index):
    type=Object_GetAttr(char,'tkType')
    custom=FFX_TK_CUSTOM
    if type==FFX_MATERIAL_STONE:
        custom=FFX_EARTHCONTROL_CUSTOM
    elif type==FFX_MATERIAL_METAL:
        custom=FFX_MAGNETIC_CUSTOM
    Trigger_Power(char,target,getByTemplate(char,custom,index),'')

def OnLift(target,char):
    print '%s lifting %s'%(char,target)
    if chargeEP(char,FFX_EP_LIFTTK)==0:
        return
    OnTKPower(char,target,1)
    AddTKCommands(char)
    count=FFX_ObjectGetAttr(char,'tiggotCount')
    gcount=Mission_GetAttr('tigCount')
    name='tig_%s_%d'%(char,count)
    pos=Get_ObjectPos(target)
    offX=30
    offY=30
    if (int(count)&1):
        offX=-30
    if (int(count)&2):
        offY=-30
    Object_SpawnAt('ffx_tiggot',(pos[0]+offX,pos[1]+offY,pos[2]),name)
    mass=Object_GetAttr(target,'mass')
    Object_SetAttr(name,'weight',mass)
    if Object_GetAttr(char,'maxMass')<500:
        speed=6-(mass/50)
    else:
        speed=6-(mass/500)
    if speed<1:
       speed=1
    Object_SetAttr(name,'speed',speed)
    initmindless(name)
    initironjaw(name)
    print 'speed=%s'%(speed)
    FFX_ObjectSetAttr(char,'tiggotCount',count+1)
    Object_SetAttr(name,'ID',gcount)
    Mission_SetAttr('tigCount',gcount+1)
    RegTimer('OnLift2',0.3,0,name,target)

def OnLift2(event):
    Trigger_Pickup(event.object,event.string)
    Object_SetSecondaryState(event.object,SCSTATE_INVISIBLE,1000,0)

def getTiggots(char):
    tigs=[]
    name='tig_%s'%(char)
    nlen=len(name)
    for obj in Mission_GetDynamicObjects():
        if Object_GetTemplate(obj)=='ffx_tiggot':
            if obj[:nlen]==name:
                tigs.append(obj)
    return tigs

def getTigByID(id):
    for obj in Mission_GetDynamicObjects():
        if Object_GetTemplate(obj)=='ffx_tiggot':
            if Object_GetAttr(obj,'ID')==id:
                return obj
    return ''

def OnThrowTo(target,char):
    print '%s throwing at %s'%(char,target)
    if chargeEP(char,FFX_EP_THROWTK)==0:
        return
    OnTKPower(char,target,2)
    tigs=getTiggots(char)
    for tig in tigs:
        Trigger_Throw(tig,Get_ObjectPos(target))
        RegTimer('tigDie',3,Object_GetAttr(tig,'ID'))
    if len(tigs)>0:
        RemoveTKCommands(char)

def OnMoveTo(target,char):
    if chargeEP(char,FFX_EP_MOVETK)==0:
        return
    OnTKPower(char,target,3)
    tigs=getTiggots(char)
    for tig in tigs:
        Trigger_Move(tig,'move',Get_ObjectPos(target),'',100)

def tigDie(event):
    tig=getTigByID(event.user)
    if tig!='':
        Object_Destroy(tig)

def OnDrop(dummy,char):
    tigs=getTiggots(char)
    for tig in tigs:
        Object_Destroy(tig)
    if len(tigs)>0:
        RemoveTKCommands(char)

def updateTK(char):
    if (Object_GetPrimaryState(char)!=PCSTATE_NORMAL) | (Object_IsAlive(char)==0):
        OnDrop(char,char)
    else:
        for tig in getTiggots(char):
           if distanceSq(tig,char)>(400*400):
               Trigger_Move(tig,'move',Get_ObjectPos(char),'',100)

#get the best object to throw at a target character
def GetTKTarget(target,minMass=200):
    minDist=0
    Mission_SetAttr('distance',0)
    bestObj=''
    for object in throwObjects:
        if Object_Exists(object):
            if Object_GetAttr(object,'mass')>=minMass:
                dist=distanceSq(target,object)-6000
                if dist<0:
                    dist=-dist
                if (minDist==0) | (dist<minDist):
                    minDist=dist
                    Mission_SetAttr('distance',dist)
                    bestObj=object
    return bestObj

def rmArrow(event):
    cshelperS.removeArrow('%sa'%(event.object))

#magnetic specials

def OnAttract(target,char):
    if chargeEP(char,FFX_EP_THROWMAG)==0:
        return
    OnTKPower(char,target,4)
    RegTimer('OnAttract2',1,0,target,char)

def OnAttract2(event):
    target=event.object
    char=event.string
    direction=Vector_Sub(Get_ObjectPos(char),Get_ObjectPos(target))
    Trigger_Force(target,direction[0],direction[1],0.5,getMagnetForce(target),TF_ABSOLUTE)

def OnRepel(target,char):
    if chargeEP(char,FFX_EP_THROWMAG)==0:
        return
    OnTKPower(char,target,4)
    RegTimer('OnRepel2',1,0,target,char)

def OnRepel2(event):
    target=event.object
    char=event.string
    direction=Vector_Sub(Get_ObjectPos(target),Get_ObjectPos(char))
    Trigger_Force(target,direction[0],direction[1],0.5,getMagnetForce(target),TF_ABSOLUTE)

def getMagnetForce(target):
    force=800*Object_GetAttr(target,'mass')
    if force>500000:
        force=50000
    if force<Object_GetAttr(target,'minForce'):
        force=Object_GetAttr(target,'minForce')
    return force

#earth control specials

def OnWall(target,char):
    if chargeEP(char,FFX_EP_EARTHWALL)==0:
        return
    js.Trigger_Move(char, "turn", Get_ObjectPos(target))
    dx=Vector_Sub(Get_ObjectPos(target),Get_ObjectPos(char))
    length=0.005+sqrt(dx[0]*dx[0]+dx[1]*dx[1])
    unitdx=(dx[0]/length,dx[1]/length,0)
    myPos=Get_ObjectPos(char)
    pos=(myPos[0]+unitdx[0]*30,myPos[1]+unitdx[1]*30,myPos[2])
    name=getDummyName()
    Object_SpawnAt('stalagmites',pos,name)
    RegTimer('OnWall2',0.1,0,name,char)

def OnWall2(event):
    target=event.object
    char=event.string
    OnTKPower(char,target,4)
    dx=Vector_Sub(Get_ObjectPos(target),Get_ObjectPos(char))
    length=0.005+sqrt(dx[0]*dx[0]+dx[1]*dx[1])
    unitdx=(dx[0]/length,dx[1]/length,0)
    pos=Get_ObjectPos(target)
    Object_SpawnAt('stalagmites',(pos[0]+16*unitdx[1],pos[1]-16*unitdx[0],pos[2]),getDummyName()) 
    Object_SpawnAt('stalagmites',(pos[0]-16*unitdx[1],pos[1]+16*unitdx[0],pos[2]),getDummyName())  
    RegTimer('OnWall3',0.2,0,target,char)

def OnWall3(event):
    target=event.object
    char=event.string
    dx=Vector_Sub(Get_ObjectPos(target),Get_ObjectPos(char))
    length=0.005+sqrt(dx[0]*dx[0]+dx[1]*dx[1])
    unitdx=(dx[0]/length,dx[1]/length,0)
    pos=Get_ObjectPos(target)
    Object_SpawnAt('stalagmites',(pos[0]+32*unitdx[1],pos[1]-32*unitdx[0],pos[2]),getDummyName())
    Object_SpawnAt('stalagmites',(pos[0]-32*unitdx[1],pos[1]+32*unitdx[0],pos[2]),getDummyName()) 

def OnSpike(target,char):
    if chargeEP(char,FFX_EP_EARTHWALL)==0:
        return
    name=getDummyName()
    Object_SpawnAt('projectile_standin',Get_ObjectPos(target),name)
    OnTKPower(char,target,4)
    RegTimer('OnSpike1',1,0,name)

def OnSpike1(event):
    target=event.object
    count=Mission_GetAttr('dummyCount')
    name=getDummyName()
    Object_SpawnAt('stalagmites',Get_ObjectPos(target),name)
    RegTimer('OnSpike2',0.05,count)
    if Object_GetTemplate(target)=='projectile_standin':
        Object_Destroy(target)

def OnSpike2(event):
    name='mobjdummy_%d'%(event.user)
    Trigger_Explosion(name,'ffx_earthspike',-1)

#################### ENERGY SHIELD ###################################

# mshield is a timer, -1 means forever

def initmetashield(char,update=0):
    Object_SetAttr(char,'mshield',0)
    if update==0:
        Mission_CustomAction('CUSTOM_SHIELD',char,char,'OnShield',5,0)
    Object_SetAttr(char,'lastHealthES',Object_GetAttr(char,'health'))
    RegDamage(char,'OnEShield')

def initmetashieldp(char,update=0):
    Object_SetAttr(char,'mshield',-1)
    Object_SetAttr(char,'lastHealthES',Object_GetAttr(char,'health'))
    RegDamage(char,'OnEShield')

def OnShield(target,char):
    Object_SetAttr(target,'mshield',20)
    Object_SetAttr(target,'ffxState',FFX_STATE_ENERGYSHIELD)
    handle=Object_PlayEffect(char,getByTemplate(char,FFX_ENERGYSHIELD_CUSTOM,1),'',FX_LOOP|FX_TRACK_OBJECT_FULL,0,'Bip01 Spine')
    Object_SetAttr(char,'fxHandle',handle)
    AI_Animate(char,getByTemplate(char,FFX_ENERGYSHIELD_CUSTOM,3))

def OnEShield(event):
    char=event.object
    damage=Object_GetAttr(char,'lastHealthES')-Object_GetAttr(char,'health')
    Object_SetAttr(char,'lastHealthES',Object_GetAttr(char,'health'))
    RegDamage(char,'OnEShield')
    if Object_GetAttr(char,'mshield')==0:
        return
    if damage>0:
        maxReturn=Object_GetAttr(char,'energyPoints')/5
        payBack=maxReturn
        if damage<payBack:
            payBack=damage
        Object_SetAttr(char,'health',Object_GetAttr(char,'health')+payBack)
        Object_SetAttr(char,'energyPoints',Object_GetAttr(char,'energyPoints')-5*payBack)
        fx=getByTemplate(char,FFX_ENERGYSHIELD_CUSTOM,2)
        if fx!='':
            Object_PlayEffect(char,fx,'',0,0,bodyPart[randint(0,4)])

def updateMShield(obj):
    if Object_GetAttr(obj,'ffxState')==FFX_STATE_ENERGYSHIELD:
        if Object_GetAttr(obj,'mshield')>0:
            Object_SetAttr(obj,'mshield',Object_GetAttr(obj,'mshield')-1)
        else:
            if Object_GetAttr(obj,'fxHandle')!=-1:
                Object_StopEffect(obj,Object_GetAttr(obj,'fxHandle'))


################### HEAVY FOOTED ########################################

def initheavyfooted(char,update=0):
    pos=Get_ObjectPos(char)
    Object_SetAttr(char,'lastY',pos[1])
    Object_SetAttr(char,'lastX',pos[0])

def heavyfootUpdate(char):
    pos=Get_ObjectPos(char)
    dist=(pos[0]-Object_GetAttr(char,'lastX'),pos[1]-Object_GetAttr(char,'lastY'),0)
    Object_SetAttr(char,'lastY',pos[1])
    Object_SetAttr(char,'lastX',pos[0])
    if (dist[0]*dist[0]+dist[1]*dist[1])>getByTemplate(char,FFX_HEAVYFOOTED_CUSTOM,2):
        Trigger_Explosion(char,getByTemplate(char,FFX_HEAVYFOOTED_CUSTOM,1),-1)

################ SCARY ####################################################

def initscary(char,update=0):
    Object_SetAttr(char,'scary',1)

def updateScary(char):
    for obj in Mission_GetDynamicObjects():
        if ((Object_GetClass(obj)&OC_CHARACTER)!=0) & ((Object_GetClass(obj)&OC_CONTROLLABLE)==0):
            if randint(1,100)<=FFX_SCARY_CHANCE:
                if distanceSq(char,obj)<(250*250):
                    goal=ai.goal.KillObjects(char)
                    addGoalToAI(obj, goal, PRI_LOW) 

############ SUPERHEALER ###########################################

def initsuperhealer(char,update=0):
    doNothing=0

def updateSuperHealer(char):
    percentage=Object_GetAttr(char,'energyPoints')/Object_GetAttr(char,'maxEnergyPoints')
    if (percentage<0) | (Object_IsAlive(char)==0):
        return
    healing=5*percentage
    ep=Object_GetAttr(char,'energyPoints')
    if healing>(ep/4):
        healing=ep/4
    health=Object_GetAttr(char,'health')
    maxHealth=Object_GetAttr(char,'maxHealth')
    health=health+healing
    if health>maxHealth:
        healing=healing+maxHealth-health
        if healing<0:
            healing=0
        health=maxHealth 
    Object_SetAttr(char,'health',health)
    Object_SetAttr(char,'energyPoints',ep-healing*4)    

############# DEGENERATIVE ########################################

def initdegenerative(char,update=0):
    Object_SetAttr(char,'lastHealthDG',Object_GetAttr(char,'health'))
    Object_SetAttr(char,'degenerate',0)
    RegDamage(char,'OnDegenerate')

def OnDegenerate(event):
    char=event.object
    damage=Object_GetAttr(char,'lastHealthDG')-Object_GetAttr(char,'health')
    if damage>5:
        Object_SetAttr(char,'degenerate',4)
    Object_SetAttr(char,'lastHealthDG',Object_GetAttr(char,'health'))
    RegDamage(char,'OnDegenerate')                            

def updateDegenerative(char):
    degen=Object_GetAttr(char,'degenerate')
    if degen>0:
        print 'degenerate:%d'%(degen)
        Trigger_Damage(char,degen)
        Object_SetAttr(char,'degenerate',degen-1)
        fx=getByTemplate(char,FFX_DEGENERATIVE_CUSTOM,1)
        if fx!='':
            Object_PlayEffect(char,fx,'',0,0,bodyPart[randint(0,4)])     

    

############# INVOLUNTARY CHANGES #################################

def initinvtransfromp(char,update=0):
    TODO=0

def initinvtransforms(char,update=0):
    TODO=0

############## PUPPET MASTERS #######################################

FFX_URBAN=0
FFX_NATURE=1

def initpuppetu(char,update=0):
    if update==0:
        temps=[]
        for template in urbanTemplates:
            Mission_CustomAction('CUSTOM_ANIMATE',char,template[0],'OnAnimateUrban',30,0)
            match=0
            for temp in temps:
                if temp==template[1]:
                    match=1
            if match==0:
                Mission_CustomAction('CUSTOM_DEANIMATE',char,template[1],'OnDeanimateUrban',30,0)
                Mission_CustomAction('CUSTOM_SUSTAIN',char,template[1],'OnSustainUrban',30,0)       
                temps.append(template[1])

def initpuppetn(char,update=0):
    if update==0:
        temps=[]
        for template in natureTemplates:
            Mission_CustomAction('CUSTOM_ANIMATE',char,template[0],'OnAnimateNature',30,0)
            match=0
            for temp in temps:
                if temp==template[1]:
                    match=1
            if match==0:
                Mission_CustomAction('CUSTOM_DEANIMATE',char,template[1],'OnDeanimateNature',30,0)
                Mission_CustomAction('CUSTOM_SUSTAIN',char,template[1],'OnSustainNature',30,0)       
                temps.append(template[1])

def OnAnimateUrban(target,char):
    OnAnimate(target,char,FFX_URBAN)

def OnAnimateNature(target,char):
    OnAnimate(target,char,FFX_NATURE)

def OnSustainUrban(target,char):
    OnSustain(target,char,FFX_URBAN)

def OnSustainNature(target,char):
    OnSustain(target,char,FFX_NATURE)

def OnDeanimateUrban(target,char):
    OnDeanimate(target,char,FFX_URBAN)

def OnDeanimateNature(target,char):
    OnDeanimate(target,char,FFX_NATURE)

def OnAnimate(target,char,type):
    if chargeEP(char,FFX_EP_ANIMATE)==0:
        return
    if type==FFX_NATURE:
        Trigger_Power(char,target,getByTemplate(char,FFX_PUPPETNATURE_CUSTOM,1),'')
    else:
        Trigger_Power(char,target,getByTemplate(char,FFX_PUPPETURBAN_CUSTOM,1),'')
    RegTimer('OnAnimate2',2,type,target)

def OnAnimate2(event):
    target=event.object
    if Object_Exists(target)==0:
        return
    name=getDummyName()
    found=getAnim(target,event.user)
    if Template_Exists(found[1])==0:
        print 'non existant template %s used in OnAnimate'%(found[1])
        return
    Object_SetAttr(target,'standOn',0)
    Object_SpawnAt(found[1],Get_ObjectPos(target),name)
    Object_SetAttr(name,'ffxState',FFX_STATE_PUPPET)
    Object_SetAttr(name,'lifespan',30)
    Object_SetAttr(name,'templateID',found[0])
    Object_SetAttr(name,'maxHealth',Object_GetAttr(target,'maxHealth'))
    Object_SetAttr(name,'health',Object_GetAttr(target,'health'))
    Object_SetAttr(name,'mass',Object_GetAttr(target,'mass'))
    RegTimer('OnAnimate3',0.1,event.user,name,target)

def OnAnimate3(event):
    name=event.object
    target=event.string
    pos=Get_ObjectPos(name)
    Trigger_Move(name,'turn',(pos[0]+randint(-50,50),pos[1]+randint(-50,50),pos[2]))
    print 'onAnimate3: target=%s name=%s'%(target,name)
    #set the AI to attack all baddies - heroes only for now
    goal = ai.goal.KillClass(OC_VILLIAN)
    addGoalToAI(name, goal, PRI_HI, '', 0,1,1)
    goal = ai.goal.KillClass(OC_MINION)
    addGoalToAI(name, goal, PRI_MED, '', 0,1,1)
    if event.user==FFX_URBAN:
        Object_Destroy(target)   
    else:
        Trigger_Damage(target,Object_GetAttr(target,'health'))

def OnSustain(target,char,type):
    if chargeEP(char,FFX_EP_SUSTAIN)==0:
        return
    if type==FFX_NATURE:
        Trigger_Power(char,target,getByTemplate(char,FFX_PUPPETNATURE_CUSTOM,2),'')
    else:
        Trigger_Power(char,target,getByTemplate(char,FFX_PUPPETURBAN_CUSTOM,2),'')
    Object_SetAttr(target,'lifespan',Object_GetAttr(target,'lifespan')+30)
    Object_SetAttr(target,'health',(Object_GetAttr(target,'health')+Object_GetAttr(target,'maxHealth'))*0.5)
    Object_SetPrimaryState(target,PCSTATE_NORMAL,10,0)
 
def OnDeanimate(target,char,type):
    if type==FFX_NATURE:
        Trigger_Power(char,target,getByTemplate(char,FFX_PUPPETNATURE_CUSTOM,3),'')
    else:
        Trigger_Power(char,target,getByTemplate(char,FFX_PUPPETURBAN_CUSTOM,3),'')
    RegTimer('OnDeanimate2',2,0,target)

def OnDeanimate2(event):
    target=event.object
    if Object_Exists(target):
        UnAnimate(target)    

def getAnim(obj,type):
    index=0
    if type==FFX_URBAN:
        temps=urbanTemplates
    else:
        temps=natureTemplates
        index=100
    template=Object_GetTemplate(obj)
    for temp in temps:
        if temp[0]==template:
            return [index,temp[1]]
        index=index+1
    return [-1,'']

def UnAnimate(obj):
    index=int(Object_GetAttr(obj,'templateID'))
    if index<100:
        oldTemp=urbanTemplates[index][0]
    else:
        oldTemp=natureTemplates[index-100][0]
    name=getDummyName()
    Object_SpawnAt(oldTemp,Get_ObjectPos(obj),name)
    Object_SetAttr(name,'health',Object_GetAttr(obj,'health'))
    if Object_GetAttr(name,'health')<=0:
        Object_SetAttr(name,'health',1)
        Trigger_Damage(name,2)
    AI_SetEnabled(obj, 0)
    Object_SetAttr(obj,'physical',0)
    Object_SetSecondaryState(obj,SCSTATE_INVISIBLE,10,0)
    Object_SetAttr(obj,'ffxState',0)
    RegTimer('UnAnimate2',4,0,obj)

#do this on a timer so that projectiles have time to leave the world before destroying the bus-men
def UnAnimate2(event):
    Object_Destroy(event.object)    

def updatePuppets():
    for obj in Mission_GetDynamicObjects():
        if Object_GetAttr(obj,'ffxState')==FFX_STATE_PUPPET:
            lifespan=Object_GetAttr(obj,'lifespan')
            lifespan=lifespan-1
            if lifespan==3:
               Object_SetPrimaryState(obj,PCSTATE_STUNNED,4,0)
            if (lifespan==0) | (Object_IsAlive(obj)==0):
                #if Object_GetAttr(obj,'health')==0:
                #    Object_SetAttr(obj,'health',1)
                UnAnimate(obj)
            else:
                Object_SetAttr(obj,'lifespan',lifespan)


############## GUARDIANS #########################################

def initguardianm(char,update=0):
    attr='%s_mshield'%(char)
    Global_RegisterAttr(attr,0)
    for obj in Mission_GetDynamicObjects():
        if ((Object_GetClass(obj) & js.OC_CHARACTER)!=0) & (obj!=char):
            if (Object_GetAttr(obj,attr)==0) & (Object_GetAttr(obj,'secondTimer')==0):
                Mission_CustomAction('CUSTOM_SHIELDM',char,obj,'OnMShield',FFX_SHIELDRANGE,0)
                Object_SetAttr(obj,attr,1)
    if update==0:
        Mission_CustomAction('CUSTOM_SHIELDM_TEAM',char,char,'OnMShieldAll',5,0)

def OnMShield(target,char):
    if chargeEP(char,FFX_EP_MINDSHIELD)==0:
        return
    Trigger_Power(char,target,getByTemplate(char,FFX_GUARDIAN_MENTAL_CUSTOM,1),'')
    Trigger_Power(target,target,getByTemplate(char,FFX_GUARDIAN_MENTAL_CUSTOM,2),'')

def OnMShieldAll(dummy,char):
    if chargeEP(char,FFX_EP_MINDSHIELD)==0:
        return
    Trigger_Power(char,char,getByTemplate(char,FFX_GUARDIAN_MENTAL_CUSTOM,1),'')
    for obj in Mission_GetDynamicObjects():
        if ((Object_GetClass(obj) & (OC_CONTROLLABLE|OC_POLICE|OC_CIVILIAN))!=0) & (obj!=char):
            if distanceSq(obj,char)<250*250:
                Trigger_Power(obj,obj,getByTemplate(char,FFX_GUARDIAN_MENTAL_CUSTOM,2),'')

def initguardianp(char,update=0):
    attr='%s_pshield'%(char)
    Global_RegisterAttr(attr,0)
    for obj in Mission_GetDynamicObjects():
        if ((Object_GetClass(obj) & js.OC_CHARACTER)!=0) & (obj!=char):
            if (Object_GetAttr(obj,attr)==0) & (Object_GetAttr(obj,'secondTimer')==0):
                Mission_CustomAction('CUSTOM_SHIELDP',char,obj,'OnPShield',FFX_SHIELDRANGE,0)
                Object_SetAttr(obj,attr,1)
    if update==0:
        Mission_CustomAction('CUSTOM_SHIELDP_TEAM',char,char,'OnPShieldAll',5,0)

def OnPShield(target,char):
    if chargeEP(char,FFX_EP_PHYSICALSHIELD)==0:
        return
    Trigger_Power(char,target,getByTemplate(char,FFX_GUARDIAN_PHYSICAL_CUSTOM,1),'')
    Trigger_Power(target,target,getByTemplate(char,FFX_GUARDIAN_PHYSICAL_CUSTOM,2),'')

def OnPShieldAll(dummy,char):
    if chargeEP(char,FFX_EP_PHYSICALSHIELD)==0:
        return
    Trigger_Power(char,char,getByTemplate(char,FFX_GUARDIAN_PHYSICAL_CUSTOM,1),'')
    for obj in Mission_GetDynamicObjects():
        if ((Object_GetClass(obj) & (OC_CONTROLLABLE|OC_POLICE|OC_CIVILIAN))!=0) & (obj!=char):
            if distanceSq(obj,char)<250*250:
                Trigger_Power(obj,obj,getByTemplate(char,FFX_GUARDIAN_PHYSICAL_CUSTOM,2),'')

################ METAMORPHIC ####################################

def initmetamorphic(char,update=0):
    initgaseousform(char,update)
    initsandstorm(char,update)
    initicecloud(char,update)

################# EPHEMERAL ####################################

def initephemeral(char,update=0):
    doNothing=0

def updateEphemeral(char):
    percentEnergy=Object_GetAttr(char,'energyPoints')/Object_GetAttr(char,'maxEnergyPoints')
    maxHealth=Object_GetAttr(char,'maxHealth')*percentEnergy
    if Object_GetAttr(char,'health') > maxHealth:
        print 'ephemeral: ep=%d, drop health from %d to %d'%(percentEnergy,Object_GetAttr(char,'health'),maxHealth)
        Object_SetAttr(char,'health',maxHealth)
        if maxHealth<=0:
            RegTimer('OnDie',0.1,0,char)
	
################ SCIENTIST ####################################

def initscientist(char,update=0):
    doNothing=0

def initcharged(char,update=0):
    doNothing=0

def initnocturna(char,update=0):
    doNothing=0

################ TELEPATHIC ###################################

def inittelepathic(char,update=0):
    doNothing=0

####################### ILLUSIONIST ###################################

def initillusionist(char,update=0):
    doNothing=0
    #todo - custom commands for illusionists: clone images and 

####################### GENERAL UTILITY FUNCTIONS ######################

#################################
#
# teleport to point by create/destroy method
#
#################################

def FFX_Teleport(char,pos):
    print 'FFX_TP name=%s'%(char)
    index=Mission_GetAttr('teleportIndex')+1
    Mission_SetAttr('teleportIndex',index+1)
    Mission_SetAttr('TP_health%d'%(index),Object_GetAttr(char,'health'))
    Mission_SetAttr('TP_energy%d'%(index),Object_GetAttr(char,'energyPoints'))
    Mission_SetAttr('TP_hero%d'%(index),Object_GetAttr(char,'heroPoints'))    
    Mission_SetAttr('TP_X%d'%(index),pos[0])
    Mission_SetAttr('TP_Y%d'%(index),pos[1])
    Mission_SetAttr('TP_Z%d'%(index),pos[2])
    print 'regTimer-ing index=%d char=%s template=%s'%(index,char,Object_GetTemplate(char))
    #RegTimer('tp2',1,index,char,Object_GetTemplate(char))
    print 'destroying'
    Object_Destroy(char)

def tp2(event):
    name=event.object
    template=event.string
    print 'ffx_TP2: name=%s template=%s'%(name,template)
    index=event.user
    pos=(Mission_GetAttr('TP_X%d'%(index)),Mission_GetAttr('TP_Y%d'%(index)),Mission_GetAttr('TP_Z%d'%(index)))
    Object_SpawnAt(template,pos,name)
    RegTimer('tp3',0.2,index,name)

def tp3(event):
    char=event.object
    index=event.user
    Object_SetAttr(char,'health',Mission_GetAttr('TP_health%d'%(index)))
    Object_SetAttr(char,'energyPoints',Mission_GetAttr('TP_energy%d'%(index)))
    Object_SetAttr(char,'heroPoints',Mission_GetAttr('TP_hero%d'%(index)))
    reInit(char)

#################################
#
# deals with hidden heroes in mid laser/explode/shapeshift
#
#################################

def getHiddenHeroes():
    return Mission_GetAttr('hiders')
   
def addHider():
    Mission_SetAttr('hiders',Mission_GetAttr('hiders')+1)
    print 'hiders=%d'%(Mission_GetAttr('hiders'))

def delHider():
    Mission_SetAttr('hiders',Mission_GetAttr('hiders')-1)
    print 'hiders=%d'%(Mission_GetAttr('hiders'))

#################################
#
# safe set/get attr functions
#
#################################

def FFX_ObjectSetAttr(char,name,value):
    Mission_SetAttr('%s_%s'%(char,name),value)

def FFX_ObjectGetAttr(char,name):
    try:
        return Mission_GetAttr('%s_%s'%(char,name))
    except:
        return 0

def FFX_TemplateSetAttr(char,name,value):
    Mission_SetAttr('%s_%s'%(Object_GetTemplate(char),name),value)

def FFX_TemplateGetAttr(char,name):
    try:
        return Mission_GetAttr('%s_%s'%(Object_GetTemplate(char),name))
    except:
        return 0

##############################
#
#utilities for adding and removing commands to all objects for simulated attack powers
#
##############################

def addCommand(command,char,fn,range):
    print 'adding command %s to %s'%(command,char)
    if FFX_ObjectGetAttr(char,command)!=0:
        return
    FFX_ObjectSetAttr(char,command,1)
    for temp in templates:
        Mission_CustomAction(command,char,temp[0],fn,range,0)
    for obj in Mission_GetDynamicObjects():
        temp=Object_GetTemplate(obj)
        match=0
        for template in templates:
            if temp==template[0]:
                match=1
        if match==0:
            Mission_CustomAction(command,char,obj,fn,range,0)

def removeCommand(command,char):
    print 'removing command %s from %s'%(command,char)
    FFX_ObjectSetAttr(char,command,0)
    for temp in templates:
        Mission_RemoveCustomAction(command,char,temp[0])
    for obj in Mission_GetDynamicObjects():
        temp=Object_GetTemplate(obj)
        match=0
        for template in templates:
            if temp==template[0]:
                match=1
        if match==0:
            Mission_RemoveCustomAction(command,char,obj)

def addCommandChars(command,char,fn,range):
    print 'adding character command %s to %s'%(command,char)
    if FFX_ObjectGetAttr(char,command)!=0:
        return
    FFX_ObjectSetAttr(char,command,1)
    for obj in Mission_GetDynamicObjects():
        if Object_GetClass(obj)&OC_CHARACTER:
            Mission_CustomAction(command,char,obj,fn,range,0)

def removeCommandChars(command,char):
    print 'removing character command %s from %s'%(command,char)
    FFX_ObjectSetAttr(char,command,0)
    for obj in Mission_GetDynamicObjects():
        if Object_GetClass(obj)&OC_CHARACTER:
            Mission_RemoveCustomAction(command,char,obj)

######################################
#
# get layout prop objects by template
#
######################################

def getObjectsByTemplate(template):
    list=[]
    for i in range(1,1000):
        obj='_impobj_%d'%(i)
        if Object_Exists(obj):
            if Object_GetTemplate(obj)==template:
                list.append(obj)
    return list


##################################################################
#
# get all heroes at start of game and restore using campaign stuff
#
##################################################################

builtinHeroes=('devil_doll','kingzero','adamzorn','anemone','law','order','thespider','chaosgirl','jimmy_jupiter','aceofswords','miss_amazing','jackied','drummond','echo','aquilus','tart_hero','hunk_hero','penny','fly5000','spidr','dd_tulpa','anemone_tulpa','biffbateman')

#returns 1 for first hero above, 2 for second etc.
#or custom tmeplate number for custom recruits

def getHeroID(char):
    template=Object_GetTemplate(char)
    i=0
    for name in builtinHeroes:
        i=i+1
        if template==name:
            return i
    if cshelperS.getMission()==69:
        numberStr=template[11:]
        return -(69+string.atoi(numberStr))
    else:
        numberStr=template[16:]
        return -string.atoi(numberStr)

#gets the template which this character started the mission with
def getOriginalTemplate(char):
    attr='id_%d'%(FFX_ObjectGetAttr(char,'originalIndex'))
    print 'gOT:attr=%s'%(attr)
    try:
        id=Mission_GetAttr(attr)
        print 'id=%d'%(id)
        template=getTemplateFromID(id)
        print 'template=%s'%(template)
        return template
    except:
        return ''

#takes number above and turns it back into a template
def getTemplateFromID(id):
    if (id>0):
        return builtinHeroes[int(id-1)]
    if cshelperS.getMission()!=69:
        return 'custom_template_%d'%(-id)
    else:
        return 'squad1_hero%d'%(-(id+69))

def storeHeroes():
    i=1
    for hero in cshelperS.getAllHeroes():
        if hero[:5]!='hero_':
            attr='id_%d'%(i)
            FFX_ObjectSetAttr(hero,'originalIndex',i)
            value=getHeroID(hero)
            Mission_SetAttr(attr,value)
            print 'storing %s: %s,%d'%(hero,attr,value)
            i=i+1
    for hero in ('hero_1','hero_2','hero_3','hero_4'):
        if Object_Exists(hero):
            attr='id_%d'%(i)
            FFX_ObjectSetAttr(hero,'originalIndex',i)
            value=getHeroID(hero)
            Mission_SetAttr(attr,value)
            print 'storing %s: %s,%d'%(hero,attr,value)
            i=i+1

def originalHeroes():
    list=[]
    for i in range(1,5):
        try:
            attr='id_%d'%(i)
            print 'retrieving: %s'%(attr)
            id=Mission_GetAttr(attr)
            template=getTemplateFromID(int(id))
            list.append(template)
        except:
            print 'cant find id %d'%(i)
    return list

##############################################
#
# solves the following problems -
# hero may be pickled by explosive/laser
# hero may be in form_X after shapeshifting/mimicking
# STRANGERS SPECIFIC - hero may be penny/alculbus separated
#
##############################################

def restoreHeroes(event):
    #force explosive/living laser back to normal
    #unPickleAll()
    #force shapechangers back to normal
    for hero in cshelperS.getAllHeroes():
        if hero[:5]=='form_':
            index=FFX_ObjectGetAttr(hero,'originalIndex')
            print '%s has index of %d'%(hero,index)
            originalName='hero_%d'%(index)
            templateID=Mission_GetAttr('id_%i'%(index))
            template=getTemplateFromID(int(templateID))
            print 'creating %s [%s]'%(originalName,template)
            morph(originalName,hero,template,0)
    #force alcubus to remerge
    if Object_Exists('alcubus') & Object_Exists('penny'):
        Object_Destroy('alcubus')
        RegTimer('OnReturn',0.3)

###################################################
#
# places custom recruits in base cutscenes at 'custom_1','custom_2', etc.
#
###################################################

def placeCustoms(max,dupe=''):
    print 'placeCustoms'
    markerIndex=1
    for index in range(20,100):
        template='custom_template_%d'%(index)
        if (Campaign_IsRecruited(template)!=0) & (template!=dupe):
            print 'isRecruited'
            if markerIndex<=max:
                print markerIndex
                marker='custom_%d'%(markerIndex)
                name='hero_%s'%(marker)
                cshelperS.spawn(name,template,marker)
                RegTimer('tempCheck',2,0,name)
                markerIndex=markerIndex+1
    RegTimer('parseLogFile',0.8)

def tempCheck(event):
    name=event.object
    #stops them doing melee idle in front of villains
    disable(name)
    if hasAttribute(name,'tempform'):
        Object_Destroy(name)