Beispiel #1
0
class Hero(DirectObject):
    def __init__(self,no):
        self.HeroStats(no)
        self.display()
       # self.displaynot()
        self.SetupEvents()
        self.Collision()
        self.Loader()
        self.SkillStatus()
        self.sec=0
        self.min=0
        self.heroPace=None
        self.timesec = OnscreenText(text = '', pos = (1.2,-0.725),fg=(1,1,1,1),mayChange=1,scale=0.05)
        self.timemin = OnscreenText(text = '', pos = (1,-0.725),fg=(1,1,1,1),mayChange=1,scale=0.05)
        self.deathtxt=OnscreenText(text="",pos=(0.5,0.9),scale=0.5)
        taskMgr.add(self.update,"update")
        taskMgr.doMethodLater(1,self.Second,"second")
        taskMgr.add(self.MousePos,"mouse")
    
    def HeroStats(self,no):
        self.char={}
        self.char=hero[no]
        self.name=self.char['name']
        self.model=Actor(MYDIRMODEL+self.char['model']+'.egg')
        self.type=None
        self.heroicon=self.char['icon'][0]
        self.skillicons=(self.char['icon'][1],self.char['icon'][2],self.char['icon'][3],self.char['icon'][4])
        self.StartPos=Point3(25,25,0)
        self.gold=4000
        self.goldrate = 1
        self.items=[-1,-1,-1,-1,-1,-1]  #each stores the no of the item
        self.itemindex=0
        self.itemname="self.itemb"
        self.range=self.char['range']
        self.strdt=self.char['strdt']
        self.agidt=self.char['agidt']
        self.intdt=self.char['intdt']
        self.type=self.char['type']
        self.Delta1=0
        self.Delta2=0
        self.Delta3=0
        self.Delta4=0
        self.Delta5=0
        self.lvl=0
        self.xp=0
        self.Input=None
        self.str=self.char['str']+(self.strdt*self.lvl)
        self.agi=self.char['agi'] + (self.agidt*self.lvl) 
        self.int=self.char['int'] +(self.intdt*self.lvl)
        self.basehp=590+self.str*19
        self.basemp=220+(self.int*13)
        self.maxhp=590 +(self.str*19)
        self.curhp=self.maxhp
        self.maxmp=220 +(self.int*13)
        self.curmp=self.maxmp
        self.armor=self.char['armor'] +(self.agi/7)
        self.atkspeed=1.5/self.agi
        if self.type=='str':
           self.TYPESTR()
        if self.type=='agi':
           self.TYPEAGI()
        if self.type=='int':
           self.TYPEINT() 
           
        self.healrate=0.003 *self.str
        self.mprate=0.02 *self.int
        self.res=0.25
        self.speed=self.char['speed']
        self.skill=0
        self.isDead=False
        self.lon=pi*(self.getX()+10)*(self.getY()+10)
        
    def Loader(self):
        self.model=Actor("models/ralph", {"run": "models/ralph-run"})
      #  self.model=Actor(MYDIRMODEL+self.model+'.egg')
        self.model.setScale(3) 
     #   self.model.setHpr(90,270,0)
        self.model.setTransparency(0.5)
     #   self.model.setScale(0.1)
        self.model.setPos(self.StartPos)
        self.model.reparentTo(render)
     #   self.model.setColor(0, 0, 0, 0)
        self.model.loop("run")
     #   self.Input=Input(self.model)
        Flame(self,self.model)
                         
#-------------------------------------------------------------Display Function---------------------------------------------------#
    def TYPESTR(self):
        self.mindamage=self.char['min']+self.str+self.Delta1
        self.maxdamage=self.char['max']+self.str+self.Delta1
        self.damage=range(int(self.mindamage),int(self.maxdamage)) 
    def TYPEAGI(self):
        self.mindamage=self.char['min']+self.agi+self.Delta1
        self.maxdamage=self.char['max']+self.agi+self.Delta1
        self.damage=range(int(self.mindamage),int(self.maxdamage)) 
    def TYPEINT(self):
        self.mindamage=self.char['min']+self.int+self.Delta1
        self.maxdamage=self.char['max']+self.int+self.Delta1
        self.damage=range(int(self.mindamage),int(self.maxdamage)) 
    def TYPE(self):
        pass
    def display(self):
        x,y,z=self.model.getX(),self.model.getY(),self.model.getZ()
        base.camera.setPos(x,y,z+180)
        base.camera.setP(-30)
        base.camera.lookAt(self.model)
        self.panel=aspect2d.attachNewNode("panel")
        self.panel.setTransparency(1)
        self.SKNode=aspect2d.attachNewNode("skl")
        self.SKNode.setTransparency(0)
        self.HP=DirectLabel(text='',parent = self.panel,text_fg=(0,0.9,0,1),frameColor=(0,0,0,0),pos=(-0.41,0,-0.850),scale=0.04)
        self.MP=DirectLabel(text='',parent = self.panel,text_fg=(0,0,0.8,1),frameColor=(0,0,0,0),pos=(-0.41,0,-0.912),scale=0.04)
        self.LVL=DirectLabel(text ="Level %d"%(self.lvl+1),parent = self.panel,text_fg=(0,0,0,1),frameColor=(0,0,0,0),pos =(-0.5,0,-0.79),scale=Sc)
        Text1(self,"Damage",-0.26,-0.02,-1)
        Text1(self,"Armor",-0.27,0.03,-1)
        Text1(self,"Str",-0.25,0.085,-1)
        Text1(self,"Agi",-0.25,0.13,-1)
        Text1(self,"Int",-0.25,0.17,-1)
        self.DAM=Text2(self,"%d-%d",(self.mindamage-self.Delta1,self.maxdamage-self.Delta1),-0.40,-0.02,-1)
        self.ARM=Text2(self,"%d",(self.armor-self.Delta2),-0.40,0.03,-1)
        self.STR=Text2(self,"%d",(self.str-self.Delta3),-0.40,0.085,-1)
        self.AGI=Text2(self,"%d",(self.agi-self.Delta4),-0.40,0.13,-1)
        self.INT=Text2(self,"%d",(self.int-self.Delta5),-0.40,0.17,-1)
        if self.Delta1!=0:
           self.damdelta=Text3(self,"%d",self.Delta1,-0.4,-0.02,-1)
        if self.Delta2!=0:
           self.armdelta=Text3(self,"%d",self.Delta2,-0.4,0.03,-1)   
        if self.Delta3!=0:
           self.strdelta=Text3(self,"%d",self.Delta3,-0.36,0.085,-1)
        if self.Delta4!=0:
           self.agidelta=Text3(self,"%d",self.Delta4,-0.44,0.13,-1)
        if self.Delta5!=0:
           self.intdelta=Text3(self,"%d",self.Delta5,-0.44,0.17,-1)
        self.hpbar = DirectWaitBar(barColor=(0,0.176470,0,1),parent = self.panel,scale=(0.3,0,0.23), frameColor=(0,0,0,1),pos = (0,0,-0.84))
        self.mpbar=DirectWaitBar(barColor=(0,0,0.6,1),parent = self.panel,scale=(0.3,0,0.23), frameColor=(0,0,0,1),pos = (0,0,-0.90))
        self.lvlbar=DirectWaitBar(barColor=(0,0,0.2,1),parent = self.panel,image='glue/lvlbar.png',image_scale=(1.1,0,0.2),scale=0.35, pos = (0,0,-0.8))
        self.lvlbar.setTransparency(1)
        self.lvlbar.detachNode()
        self.skbtn1=DirectButton(image=self.skillicons[0]+'.png',parent=self.SKNode,pos=(posx-1.3,0,posy-1.2),pad=(-0.1,-0.1),scale=biconscale,command=self.SkillNo,extraArgs=[0])
        self.skbtn2=DirectButton(image=self.skillicons[1]+'.png',parent=self.SKNode,pos=(posx-1.3+0.14,0,posy-1.2),pad=(-0.1,-0.1),scale=biconscale,command=self.SkillNo,extraArgs=[1])
        self.skbtn3=DirectButton(image=self.skillicons[2]+'.png',parent=self.SKNode,pos=(posx-1.3+0.28,0,posy-1.2),pad=(-0.1,-0.1),scale=biconscale,command=self.SkillNo,extraArgs=[2])
        self.skbtn4=DirectButton(image=self.skillicons[3]+'.png',parent=self.SKNode,pos=(posx-1.3+0.42,0,posy-1.2),pad=(-0.1,-0.1),scale=biconscale,command=self.SkillNo,extraArgs=[3])
        self.b2 = DirectButton(text ="dam",parent = self.panel,pos=(-0.5,0,0),enableEdit=1,scale=(0.25,0,0.1),command=self.hurt,extraArgs=[200])
        self.b3 = DirectButton(text ="",image='tome.tga',pos=(0.5,0,0),frameColor=(0,0,0,0),pad=(-0.1,-0.1),enableEdit=1,scale=(0.07,0,0.07),command=self.itemBuy,extraArgs=[0,300])
        self.GOLD=OnscreenText(text='',fg=(1,1,1,1),pos=(1.225,-0.84),scale=0.05)  
       # self.escapeEvent = OnscreenText(text=HELPTEXT, font = font,style=1, fg=(1,1,1,1), pos=(-0.82, -0.725),align=TextNode.ALeft, scale = .045)                         
                                
    def displaynot(self):
        self.panel.detachNode()
        self.SKNode.detachNode()
#------------------------------------------------------------Set Functions--------------------------------------------------------#    
    def setDamage(self,amt):
        if delta >1:
         self.damage+=delta
        else:
         self.damage=self.damage+self.damage*delta
    
    def Delta(self,amt1,amt2,amt3,amt4,amt5):          
        if amt1!=0:                                #After changes occur for that period the taks sets the values to zero
           self.Delta1+=amt1
        if amt2!=0:   
           self.Delta2+=amt2
        if amt3!=0:   
           self.Delta3+=amt3
        if amt4!=0:   
           self.Delta4+=amt3
        if amt5!=0:   
           self.Delta5+=amt3
        
    def hurt(self,amt):
         if self.curhp>0:
            if self.curhp<=self.maxhp:           
               self.curhp-=amt*1
    def hurtMag(self,delta):
          if self.curhp>0:
            if self.curhp<=self.maxhp:           
               self.curhp-=delta*self.res          
    def heal(self,delta):
        if self.curhp!=self.maxhp:
           if self.curhp < self.maxhp:                    
              self.curhp+=delta
        
    def replenish(self,delta):
        if self.curmp!=self.maxmp:
           if self.curmp < self.maxmp:
              if self.curmp>0:                    
                 self.curmp+=delta
            
    def manaspend(self,amt):
        if self.curmp>amt:
           self.curmp-=amt
                    
    def setSpeed(self,delta):
        if delta > 1:
           self.speed+=delta
        else:
           self.speed=self.speed+self.speed*delta 
    def setArmor(self,delta):            
        self.armor+=delta
    def setPos(self,x,y,z):
        self.model.setPos(x,y,z)
    def setHPColor(self):
        if self.curhp<0.25*self.maxhp:
           self.hpbar['barColor']=(1,0,0,1)
        elif self.curhp<0.5*self.maxhp:
           self.hpbar['barColor']=(1,0.5,0,1)
        elif self.curhp<0.75*self.maxhp:
           self.hpbar['barColor']=(1,1,0,1)
        else:
           self.hpbar['barColor']=(0,0.176470,0,1)
        
#-------------------------------------------------------------Setup Keys And EVENTS--------------------------------------------------#
    def SetupEvents(self):
        self.dt=0
        self.keyMap = {"left":0, "right":0, "forward":0}
        self.isMoving = False
        self.Change=False
        self.Animate=False
        self.pos3d=None
        self.target=Point3()
        self.dist=0
        self.floater = NodePath(PandaNode("floater"))
        self.floater.reparentTo(render)
        self.Text = OnscreenText(text="Set PanRate",pos=(-1.25,-0.15),scale=0.1)
        self.slider = DirectSlider(range=(20,100), value=50, pageSize=2, pos=(-1.25,0,-0.2),scale= (0.2,0.2,0.2), command=self.setScrollSpeed)
        self.dumm=loader.loadModel("models/panda.egg")
        self.dumm.reparentTo(render)
        self.dumm.setTag("Unit",'1')
        self.dumm.setPos(0,0,0)
        self.mini=0
        self.x1,self.y1=self.model.getX(),self.model.getY()
        self.x2,self.y2=self.dumm.getX(),self.dumm.getY()
        self.fired=False
        self.atk=Attack(self.model,self.dumm,1.4)
        self.accept("arrow_left", self.setKey1, ["left",1,True])
        self.accept("arrow_right", self.setKey1, ["right",1,True])
        self.accept("arrow_up", self.setKey1, ["forward",1,True])
        self.accept("arrow_left-up", self.setKey1, ["left",0,False])
        self.accept("arrow_right-up", self.setKey1, ["right",0,False])
        self.accept("arrow_up-up", self.setKey1, ["forward",0,False])
        self.accept("mouse1",self.ObjectClick)
        self.accept("mouse3",self.MoveHero)
        
    def MoveHero(self):
        self.startR=self.model.getHpr()
        self.target=self.mpos3d
        x2,y2,z2=self.target.getX(),self.target.getY(),self.target.getZ()
        h1,p1,r1=self.model.getH(),self.model.getP(),self.model.getR()
        self.dist=sqrt(pow(self.x1-x2,2)+pow(self.y1-y2,2))
        self.sptime=self.dist/(self.speed)
        self.hall=270-degrees(y2/x2)
       # self.model.setPos(self.model,self.spd,0,self.spd)
        self.Inter=LerpPosHprInterval(self.model,self.sptime,pos=self.target ,startPos=self.model.getPos(),startHpr=self.startR,hpr=self.startR)#(h1,p1,self.hall))
        #Inter2=Func(self.model.lookAt(self.target),Wait(0.3))
        self.heroPace = Sequence(self.Inter,name="heroPace")
        self.heroPace.start()
        
    def Collision(self):
        self.mpos3d=0
        self.plane = Plane(Vec3(0, 0, 1), Point3(0, 0, 0)) 
        base.cTrav = CollisionTraverser()
        self.collHandler = CollisionHandlerQueue()
        self.pickerNode = CollisionNode('mouseRay')
        self.pickerNP = camera.attachNewNode(self.pickerNode)
        self.pickerNode.setFromCollideMask(GeomNode.getDefaultCollideMask())
        self.pickerRay = CollisionRay()
        self.pickerNode.addSolid(self.pickerRay)
        base.cTrav.addCollider(self.pickerNP, self.collHandler)
    
    def ObjectClick(self): 
        if base.mouseWatcherNode.hasMouse():    
           mpos = base.mouseWatcherNode.getMouse()   
        self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())    
        base.cTrav.traverse(render)   # Assume for simplicity's sake that myHandler is a CollisionHandlerQueue.   
        if self.collHandler.getNumEntries() > 0:      # This is so we get the closest object.      
            self.collHandler.sortEntries()      
            self.pickedObj = self.collHandler.getEntry(0).getIntoNodePath()      
            self.pickedObj = self.pickedObj.findNetTag('Unit')
            if not self.pickedObj.isEmpty():
                self.Attack(self.pickedObj.getPos())
                #Handles the object       
                
    def setKey1(self, key, value,value2):
        self.keyMap[key] = value
        self.Change=value2
    
    def checkKeys(self):
        if (self.keyMap["left"]!=0):
            self.model.setH(self.model.getH() + self.dt*300)
        if (self.keyMap["right"]!=0):
            self.model.setH(self.model.getH() - self.dt*300)
        if (self.keyMap["forward"]!=0):
            self.model.setX(self.model, +(self.dt*25*SPEED))  
        
    def checkAnim(self):
        if self.Change:                    #(self.keyMap["forward"]!=0) or (self.keyMap["left"]!=0) or (self.keyMap["right"]!=0):
            if self.isMoving is False:
                self.model.loop("run")
                self.isMoving = True
        else:
            if self.isMoving:
                self.model.stop()
                self.model.pose("walk",5)
                self.isMoving = False
                
        if self.Animate:
            pass
    def Attack(self,pos):
        self.atk.setWeil(self.model)
        self.atk.setTarg(self.dumm)
        self.atk.setDist(self.mini)
        if self.mini<=60:
           self.Animate=self.atk.ATT()
        else:
           messenger.send('mouse3')
           if self.mini<=60:
              self.atk.ATT()
    
    def setScrollSpeed(self):
        SCROLLSPEED=self.slider['value']
    def UnSetupEvents(self):
        self.ignore("arrow_left")
        self.ignore("arrow_right")
        self.ignore("arrow_up")
        self.ignore("arrow_left-up")
        self.ignore("arrow_right-up")
        self.ignore("arrow_up-up")
        self.ignore("enter")
        self.ignore("mouse1")
        self.ignore("mouse3")
        taskMgr.remove("update")
        taskMgr.remove("second")
        taskMgr.remove("mouse")
#--------------------------------------------------------------Return Functions------------------------------------------------------#         
    def getDamage(self):
        return self.damage
    def getPos(self):
        return self.model.getPos() 
    def getX(self):
        return self.model.getX()
    def getY(self):
        return self.model.getY()
    def getZ(self):
        return self.model.getZ()
    def getlvl(self):
        return self.lvl
    def getModel(self):
        return self.model
    def gainxp(self,unit):
        self.xp+=unit
    
    def gainGold(self,gain):
        self.gold+=gain
    def sendTime(self,min,sec):
        self.min=min
        self.sec=sec
#----------------------------------------------------------------ITEM FUNCTIONS--------------------------------------------------------#        
    def itemBuy(self,arg,cost):
           if self.gold>=0:
              if self.itemindex<=5:
                 del self.items[self.itemindex]  
                 self.items.insert(self.itemindex,arg)
                 self.gainGold(-cost)
                 if self.items[self.itemindex]!=-1:
                    if self.itemindex==0:
                       self.itm0= aspect2d.attachNewNode("item0")
                       DirectButton(text ="",parent=self.itm0,image='tome.tga',pos=(0+0.12*self.itemindex,0,-0.90),pad=(-0.1,-0.1),scale=(0.05,0,0.05),extraArgs=[arg,cost],command=self.itemSold)#,commandButtons=DGG.RMB)
                    if self.itemindex==1:
                       self.itm1= aspect2d.attachNewNode("item1")
                       DirectButton(text ="",parent=self.itm1,image='tome.tga',pos=(0+0.12*self.itemindex,0,-0.90),pad=(-0.1,-0.1),scale=(0.05,0,0.05),extraArgs=[arg,cost],command=self.itemSold)#,commandButtons=DGG.RMB)   
                    if self.itemindex==2:
                       self.itm2= aspect2d.attachNewNode("item2")
                       DirectButton(text ="",parent=self.itm2,image='tome.tga',pos=(0+0.12*self.itemindex,0,-0.90),pad=(-0.1,-0.1),scale=(0.05,0,0.05),extraArgs=[arg,cost],command=self.itemSold)#,commandButtons=DGG.RMB)
                    if self.itemindex==3:
                       self.itm3= aspect2d.attachNewNode("item3")
                       DirectButton(text ="",parent=self.itm3,image='tome.tga',pos=(0+0.12*self.itemindex,0,-0.90),pad=(-0.1,-0.1),scale=(0.05,0,0.05),extraArgs=[arg,cost],command=self.itemSold)#,commandButtons=DGG.RMB)
                    if self.itemindex==4:
                       self.itm4= aspect2d.attachNewNode("item4")
                       DirectButton(text ="",parent=self.itm4,image='tome.tga',pos=(0+0.12*self.itemindex,0,-0.90),pad=(-0.1,-0.1),scale=(0.05,0,0.05),extraArgs=[arg,cost],command=self.itemSold)#,commandButtons=DGG.RMB)
                    if self.itemindex==5:
                       self.itm5= aspect2d.attachNewNode("item5")
                       DirectButton(text ="",parent=self.itm5,image='tome.tga',pos=(0+0.12*self.itemindex,0,-0.90),pad=(-0.1,-0.1),scale=(0.05,0,0.05),extraArgs=[arg,cost],command=self.itemSold)#,commandButtons=DGG.RMB)                        
                    self.itemindex+=1
                    
              else:
                  Error("No Empty Slots")
                 
           else:
               Error("No Gold")
               
    def itemSold(self,itemtosell,cost):
        self.ind=self.items.index(itemtosell)
        del self.items[self.ind]  
        self.items.insert(self.ind,-1)
        self.gainGold(cost/2)
        if self.ind==0:
           self.itm0.detachNode()
        if self.ind==1:
           self.itm1.detachNode()
        if self.ind==2:
           self.itm2.detachNode()
        if self.ind==3:
           self.itm3.detachNode()
        if self.ind==4:
           self.itm4.detachNode() 
        if self.ind==5:
           self.itm5.detachNode()        
        self.itemindex-=1     
        
        
        
        
        
        
    def lvlup(self):
        self.lvl+=1
        self.str=self.char['str'] +(self.strdt*self.lvl)
        self.str=self.char['str'] +(self.agidt*self.lvl)
        self.int=self.char['int'] +(self.intdt*self.lvl)
        self.hpgain=(self.strdt+self.Delta1)*19
        self.mpgain=(self.intdt+self.Delta3)*13
        self.maxhp=590+self.str*19
        self.maxmp=290+self.int*13      #some error here
        self.heal(self.hpgain)
        self.replenish(self.mpgain)
        self.xp =0
#---------------------------------------------------------SKILL FUNCTIONS-------------------------------------#    
    def SkillStatus(self):
        self.sp1=0
        self.sp2=0
        self.sp3=0
        self.sp4=0
        self.sp1dam=0
        self.sp2dam=0
        self.sp3dam=0
        self.sp4dam=0
        if self.sp1==1:
           self.range=400
           self.sp1dam=130
           self.raduis=100
           self.sp2dam=30
           self.sp3dam=90
           self.pulses=6
           
    def SkillNo(self,arg):
        if arg==0:
           if self.curmp>=100:
              self.accept("mouse1",Blink,extraArgs=[self])
              self.skbtn1['image']='cancel.png'
        #      self.skbtn1['command']=self.setOpen()
           else:
              Error("NO MANA") 
        elif arg==1:
             StatUp(self)
        elif arg==2:
             StatDn(self)
        else: 
             Ulti(self)
    
    def setOpen(self):
        self.open=False
        self.ignore("mouse1")
        self.skbtn1['image']=self.skillicons[0]+'.png'
        self.accept("mouse1",self.ObjectClick)
    
    
    
    
    
    
#----------------------------------------TASK FUNCTIONS------------------------------------------------------#
    def Delay(self,task):
        self.Delta(-2,-2,-2)
        return task.done    
    def Second(self,task):
        self.gainGold(self.goldrate)
        self.heal(self.healrate)
        self.replenish(self.mprate)
        return task.again
        
    def update(self,task):
        self.timemin.setText(str(self.min))
        self.timesec.setText(str(self.sec))
        self.str=self.char['str'] +(self.strdt*self.lvl)+self.Delta3        
        self.agi=self.char['agi'] + (self.agidt*self.lvl)+self.Delta4
        self.int=self.char['int']+(self.intdt*self.lvl)+self.Delta5
        self.hpgain=(self.strdt+self.Delta1)*19
        self.mpgain=(self.intdt+self.Delta3)*13
        self.maxhp=590+self.str*19
        self.maxmp=290+self.int*13      #some error here
      #  self.heal(self.hpgain)
     #   self.replenish(self.mpgain)
        self.armor=self.char['armor'] +(self.agi/7)+self.Delta2
        self.atkspeed=1.5/self.agi
        if self.type=='str':
           self.TYPESTR()
        if self.type=='agi':
           self.TYPEAGI()
        if self.type=='int':
           self.TYPEINT() 
        self.healrate=0.03 *self.str
        self.mprate=0.02 *self.int
        self.GOLD.setText(str(self.gold))
        self.hpbar['range']=int(self.maxhp)
        self.hpbar['value']=int(self.curhp)
        self.mpbar['range']=int(self.maxmp)
        self.mpbar['value']=int(self.curmp)
        self.lvlbar['range']=int(10*self.lvl)
        self.lvlbar['value']=int(self.xp)
        self.HP['text']="HP"+str(int(self.curhp))+"/"+str(int(self.maxhp))
        self.MP['text']="MP"+str(int(self.curmp))+"/"+str(int(self.maxmp))
        self.DAM['text']=str(int(self.mindamage))+'-'+str(int(self.maxdamage))
        self.ARM['text']=str(int(self.armor))
        self.STR['text']=str(int(self.str))
        self.AGI['text']=str(int(self.agi))
        self.INT['text']=str(int(self.int))
        self.LVL['text']="LEVEL "+str(int(self.lvl))
        
        if self.xp>=20*20:
           self.lvlup()   
        if self.curhp<=0:
           taskMgr.add(self.Death,"death")
        self.x1,self.y1=self.model.getX(),self.model.getY()
        self.x2,self.y2=self.dumm.getX(),self.dumm.getY()
        self.mini=sqrt(pow(self.x1-self.x2,2)+pow(self.y1-self.y2,2))
        Debug2(self,str(self.mini))
        elapsed = globalClock.getDt()
        self.dt=elapsed
        self.setHPColor()
        self.checkAnim()
        self.checkKeys()
       #     base.camera.lookAt(self.model)         
       # self.floater.setPos(self.model.getPos())
       # base.camera.lookAt(self.floater)      
        return task.cont
    
    def Death(self,task):
        self.isDead=True
        Debug2(self,str(task.time))
        if self.isDead==True:
           self.model.reparentTo(hidden)
           self.panel.detachNode()
           self.deathtime=(self.lvl+1)*3
           self.isDead=False
        if int(task.time)==self.deathtime:
            self.model.setPos(self.StartPos)
            self.model.reparentTo(render)
            self.curhp=self.maxhp
            self.model.loop("walk")
            self.display()
            taskMgr.remove("death")
          #  self.deathtxt.destroy()
        return task.cont
    
    def MousePos(self, task): #This Took me 1.5 Months to Learn
        if base.mouseWatcherNode.hasMouse(): 
           mpos = base.mouseWatcherNode.getMouse() 
           self.mpos3d = Point3() 
           nearPoint = Point3() 
           farPoint = Point3()                                   
           base.camLens.extrude(mpos, nearPoint, farPoint)
        if self.plane.intersectsLine(self.mpos3d, render.getRelativePoint(camera, nearPoint),render.getRelativePoint(camera, farPoint)):
           pass    
        return task.again  
    
    def destroy(self):
        self.panel.detachNode()
        self.t1.destroy()
        self.t2.destroy()
        self.model.remove()
        self.timesec.destroy()
        self.timemin.destroy()
        self.UnsetupEvents()
Beispiel #2
0
class MoveCursor(object):
    _EDGES      = 40
    _zPos       = 0
    _movingUp   = False
    _movingDown = False
    _color      = Vec4(0.3, 0.3, 0.8, 1)
    _currentPos = Vec3(0, 0, 0)
    
    def __init__(self, parent, entity, foot=1):
        # We keep a reference to the entity
        self.entity = entity
        
        # Setup the components of the cursor
        self._moveRadCircleNP  = NodePath("Movement Radius Node")
        self._moveLine         = LineSegs()
        self._moveLineNP       = NodePath("Movement Direction Line Node")
        self._moveZLine        = LineSegs()
        self._moveZLineNP      = NodePath("Movement Z Line Node")
        self._moveZFootNP      = NodePath("Movement Z Foot Node")
        self._moveFootCircle   = LineSegs()
        self._moveFootCircleNP = NodePath("Movement Foot Circle Node")
        self._np = NodePath("Movement Node")
        
        self.aaLevel = 16
        self.parent = parent
        self.start  = Vec3(0, 0, 0)
        self.moveRad = entity.moveRadius
        self.footRad = foot
        self.plane = Plane(Vec3(0, 0, 1), Point3(0, 0, 0))
        
        if self.aaLevel > 0:
            self._np.setAntialias(AntialiasAttrib.MLine, self.aaLevel)
        
        x = 0
        y = 0
        z = 0
        # Draw movement radius
        moveRadLine = LineSegs()
        moveRadLine.setThickness(1)
        moveRadLine.setColor(self._color)
        moveRadLine.moveTo(self.moveRad, 0, 0)
        for i in range(self._EDGES + 1):
            newX = (self.moveRad * math.cos((2*math.pi/self._EDGES)*i))
            newY = (self.moveRad * math.sin((2*math.pi/self._EDGES)*i))
            moveRadLine.drawTo(newX, newY, 0)
        moveRadGeom = moveRadLine.create()
        self._moveRadCircleNP = NodePath(moveRadGeom)
        self._moveRadCircleNP.reparentTo(self._np)
        
        # Draw movement foot circle
        self._moveFootCircle.setThickness(1)
        self._moveFootCircle.setColor(self._color)
        self._moveFootCircle.moveTo(self.footRad, 0, 0)
        for i in range(self._EDGES):
            newX = (self.footRad * math.cos((2*math.pi/self._EDGES)*i))
            newY = (self.footRad * math.sin((2*math.pi/self._EDGES)*i))
            self._moveFootCircle.drawTo(newX, newY, 0)
        self._moveFootCircle.drawTo(self.footRad, 0, 0)
        moveFootCircleGeom = self._moveFootCircle.create()
        self._moveFootCircleNP = NodePath(moveFootCircleGeom)
        self._moveFootCircleNP.reparentTo(self._np)
        
        # Draw movement direction line
        self._moveLine.setThickness(1)
        self._moveLine.setColor(self._color)
        self._moveLine.moveTo(0, 0, 0)
        self._moveLine.drawTo(x, y, z) 
        self.moveLineGO = self._moveLine.create(True)
        self._moveLineNP = NodePath(self.moveLineGO)
        self._moveLineNP.reparentTo(self._np)
        
    def updateMovePos(self, Task):
        # endPos must be transformed in the the coord sys of the model
        m_pos = self.getMouseXY()
        if m_pos is not None:
            # Transform current mouse pos
            endPos = self.parent.getRelativePoint(render, m_pos)
            
            # Adjust Z coord if needed
            if self._movingUp:
                self._zPos += 0.1
            elif self._movingDown:
                self._zPos -= 0.1
            endPos.setZ(self._zPos)
            
            # Check if we're trying to move too far, if not update pos
            dist = math.sqrt(endPos.getX()**2 + endPos.getY()**2 + 2*(endPos.getZ()**2))
            if dist <= self.moveRad:
                self._moveLine.setVertex(1, endPos)
                self._moveFootCircleNP.setPos(endPos)
                #self._currentPos = self.parent.getRelativePoint(self.parent, endPos)
                #print("endPos=%s"%endPos)
                #print("myRelPos=%s"%self._currentPos)
                self._currentPos = endPos
                
        return Task.cont
        
    def getMouseXY(self):
        # NOTE - this returns the mouse pos in the ships coord sys
        if base.mouseWatcherNode.hasMouse():
            mpos = base.mouseWatcherNode.getMouse()
            pos3d = Point3()
            nearPoint = Point3()
            farPoint = Point3()
            base.camLens.extrude(mpos, nearPoint, farPoint)
            if self.plane.intersectsLine(pos3d,
                render.getRelativePoint(camera, nearPoint),
                render.getRelativePoint(camera, farPoint)):
                    return pos3d
        return None
        
    def getPosition(self):
        return self._currentPos
    
    def startDrawing(self):
        self._np.reparentTo(self.parent)
        taskMgr.add(self.updateMovePos, 'Movement Indicator Update Task')
        
    def stopDrawing(self):
        taskMgr.remove('Movement Indicator Update Task')
        self._np.detachNode()
Beispiel #3
0
class CameraHandler(DirectObject.DirectObject):
    def __init__(self, showbase):
        self.showbase = showbase
        self.showbase.disableMouse()
        # This disables the default mouse based camera control used by panda. This default control is awkward, and won't be used.

        self.showbase.camera.setPos(0, -150, 200)
        self.showbase.camera.lookAt(0, 0, 0)
        # Gives the camera an initial position and rotation.

        self.mx, self.my = 0, 0
        # Sets up variables for storing the mouse coordinates

        self.orbiting = False
        # A boolean variable for specifying whether the camera is in orbiting mode. Orbiting mode refers to when the camera is being moved
        # because the user is holding down the right mouse button.

        self.target = Vec3()
        # sets up a vector variable for the camera's target. The target will be the coordinates that the camera is currently focusing on.

        self.camDist = 150
        # A variable that will determine how far the camera is from it's target focus

        self.panRateDivisor = 10
        # This variable is used as a divisor when calculating how far to move the camera when panning. Higher numbers will yield slower panning
        # and lower numbers will yield faster panning. This must not be set to 0.

        self.panZoneSize = .1
        # This variable controls how close the mouse cursor needs to be to the edge of the screen to start panning the camera. It must be less than 1,
        # and I recommend keeping it less than .2

        self.panLimitsX = Vec2(-1000, 1000)
        self.panLimitsY = Vec2(-1000, 1000)
        # These two vairables will serve as limits for how far the camera can pan, so you don't scroll away from the map.

        self.maxZoomOut = 500
        self.maxZoomIn = 25
        #These two variables set the max distance a person can zoom in or out

        self.orbitRate = 75
        # This is the rate of speed that the camera will rotate when middle mouse is pressed and mouse moved
        # recommended rate 50-100

        self.setTarget(0, 0, 0)
        # calls the setTarget function to set the current target position to the origin.

        self.turnCameraAroundPoint(0, 0)
        # calls the turnCameraAroundPoint function with a turn amount of 0 to set the camera position based on the target and camera distance

        self.accept("mouse2", self.startOrbit)
        # sets up the camrea handler to accept a right mouse click and start the "drag" mode.

        self.accept("mouse2-up", self.stopOrbit)
        # sets up the camrea handler to understand when the right mouse button has been released, and ends the "drag" mode when
        # the release is detected.

        self.storeX = 0
        self.storeY = 0
        # for storing of the x and y for the orbit

        # The next pair of lines use lambda, which creates an on-the-spot one-shot function.

        self.accept("wheel_up", self.zoomIn)
        # sets up the camera handler to detet when the mouse wheel is rolled upwards and uses a lambda function to call the
        # adjustCamDist function  with the argument 0.9

        self.accept("wheel_down", self.zoomOut)
        # sets up the camera handler to detet when the mouse wheel is rolled upwards and uses a lambda function to call the
        # adjustCamDist function  with the argument 1.1

        # Keys array (down if 1, up if 0)
        self.keys = {"cam-left": 0, "cam-right": 0, "cam-up": 0, "cam-down": 0}

        # Using Arrow Keys
        self.accept("arrow_left", self.setValue, [self.keys, "cam-left", 1])
        self.accept("arrow_right", self.setValue, [self.keys, "cam-right", 1])
        self.accept("arrow_up", self.setValue, [self.keys, "cam-up", 1])
        self.accept("arrow_down", self.setValue, [self.keys, "cam-down", 1])
        self.accept("arrow_left-up", self.setValue, [self.keys, "cam-left", 0])
        self.accept("arrow_right-up", self.setValue,
                    [self.keys, "cam-right", 0])
        self.accept("arrow_up-up", self.setValue, [self.keys, "cam-up", 0])
        self.accept("arrow_down-up", self.setValue, [self.keys, "cam-down", 0])

        self.keyPanRate = 1.5
        # pan rate for when user presses the arrow keys

        # set up plane for checking collision with for mouse-3d world
        self.plane = Plane(Vec3(0, 0, 1), Point3(0, 0, 0))

    def destroy(self):
        self.ignoreAll()

    def setValue(self, array, key, value):
        array[key] = value

    def clamp(self, val, minVal, maxVal):
        val = min(max(val, minVal), maxVal)
        return val

    def zoomOut(self):
        if self.camDist <= self.maxZoomOut:
            self.adjustCamDist(1.1)
        return True

    def zoomIn(self):
        if self.camDist >= self.maxZoomIn:
            self.adjustCamDist(0.9)
        return True

    def turnCameraAroundPoint(self, deltaX, deltaY):
        # This function performs two important tasks. First, it is used for the camera orbital movement that occurs when the
        # right mouse button is held down. It is also called with 0s for the rotation inputs to reposition the camera during the
        # panning and zooming movements.
        # The delta inputs represent the change in rotation of the camera, which is also used to determine how far the camera
        # actually moves along the orbit.

        newCamHpr = Vec3()
        newCamPos = Vec3()
        # Creates temporary containers for the new rotation and position values of the camera.

        camHpr = self.showbase.camera.getHpr()
        # Creates a container for the current HPR of the camera and stores those values.

        newCamHpr.setX(camHpr.getX() + deltaX)
        newCamHpr.setY(self.clamp(camHpr.getY() - deltaY, -85, -10))
        newCamHpr.setZ(camHpr.getZ())
        # Adjusts the newCamHpr values according to the inputs given to the function. The Y value is clamped to prevent
        # the camera from orbiting beneath the ground plane and to prevent it from reaching the apex of the orbit, which
        # can cause a disturbing fast-rotation glitch.

        self.showbase.camera.setHpr(newCamHpr)
        # Sets the camera's rotation to the new values.

        angleradiansX = newCamHpr.getX() * (math.pi / 180.0)
        angleradiansY = newCamHpr.getY() * (math.pi / 180.0)
        # Generates values to be used in the math that will calculate the new position of the camera.

        newCamPos.setX(self.camDist * math.sin(angleradiansX) *
                       math.cos(angleradiansY) + self.target.getX())
        newCamPos.setY(-self.camDist * math.cos(angleradiansX) *
                       math.cos(angleradiansY) + self.target.getY())
        newCamPos.setZ(-self.camDist * math.sin(angleradiansY) +
                       self.target.getZ())
        self.showbase.camera.setPos(newCamPos.getX(), newCamPos.getY(),
                                    newCamPos.getZ())
        # Performs the actual math to calculate the camera's new position and sets the camera to that position.
        #Unfortunately, this math is over my head, so I can't fully explain it.

        self.showbase.camera.lookAt(self.target.getX(), self.target.getY(),
                                    self.target.getZ())
        # Points the camera at the target location.

    def getTarget(self):
        return self.target
        # returns the cur

    def setTarget(self, x, y, z):
        #This function is used to give the camera a new target position.
        x = self.clamp(x, self.panLimitsX.getX(), self.panLimitsX.getY())
        self.target.setX(x)
        y = self.clamp(y, self.panLimitsY.getX(), self.panLimitsY.getY())
        self.target.setY(y)
        self.target.setZ(z)
        # Stores the new target position values in the target variable. The x and y values are clamped to the pan limits.

    def setPanLimits(self, xMin, xMax, yMin, yMax):
        # This function is used to set the limitations of the panning movement.

        self.panLimitsX = (xMin, xMax)
        self.panLimitsY = (yMin, yMax)
        # Sets the inputs into the limit variables.

    def startOrbit(self):
        # This function puts the camera into orbiting mode.

        # Get windows properties
        props = WindowProperties()
        # Set Hide Cursor Property
        #props.setCursorHidden(True)
        # Set properties
        self.showbase.win.requestProperties(props)
        # hide cursor

        if self.showbase.mouseWatcherNode.hasMouse():
            # We're going to use the mouse, so we have to make sure it's in the game window. If it's not and we try to use it, we'll get
            # a crash error.
            mpos = self.showbase.mouseWatcherNode.getMouse()
            self.storeX = mpos.getX()
            self.storeY = mpos.getY()
            # take current cursor values

        self.showbase.win.movePointer(0,
                                      self.showbase.win.getXSize() / 2,
                                      self.showbase.win.getYSize() / 2)
        # set to center
        self.mx = 0
        self.my = 0

        self.orbiting = True
        # Sets the orbiting variable to true to designate orbiting mode as on.

    def stopOrbit(self):
        # This function takes the camera out of orbiting mode.

        self.orbiting = False
        # Sets the orbiting variable to false to designate orbiting mode as off.

        self.showbase.win.movePointer(
            0, int((self.storeX + 1.0) / 2 * self.showbase.win.getXSize()),
            int(self.showbase.win.getYSize() -
                ((self.storeY + 1.0) / 2 * self.showbase.win.getYSize())))
        # set to taken cursor values from startOrbit
        if self.showbase.mouseWatcherNode.hasMouse():
            # We're going to use the mouse, so we have to make sure it's in the game window. If it's not and we try to use it, we'll get
            # a crash error.
            mpos = self.showbase.mouseWatcherNode.getMouse()
            self.mx = mpos.getX()
            self.my = mpos.getY()

        # Get windows properties
        props = WindowProperties()
        # Set Hide Cursor Property
        props.setCursorHidden(False)
        # Set properties
        self.showbase.win.requestProperties(props)
        # reshow cursor

    def adjustCamDist(self, distFactor):
        # This function increases or decreases the distance between the camera and the target position to simulate zooming in and out.
        # The distFactor input controls the amount of camera movement.
        # For example, inputing 0.9 will set the camera to 90% of it's previous distance.

        self.camDist = self.camDist * distFactor
        # Sets the new distance into self.camDist.

        self.turnCameraAroundPoint(0, 0)
        # Calls turnCameraAroundPoint with 0s for the rotation to reset the camera to the new position.

    def camMoveTask(self, dt):
        # This task is the camera handler's work house. It's set to be called every frame and will control both orbiting and panning the camera.

        if self.showbase.mouseWatcherNode.hasMouse():
            # We're going to use the mouse, so we have to make sure it's in the game window. If it's not and we try to use it, we'll get
            # a crash error.

            mpos = self.showbase.mouseWatcherNode.getMouse()
            # Gets the mouse position

            if self.orbiting:
                # Checks to see if the camera is in orbiting mode. Orbiting mode overrides panning, because it would be problematic if, while
                # orbiting the camera the mouse came close to the screen edge and started panning the camera at the same time.

                self.turnCameraAroundPoint(
                    (self.mx - mpos.getX()) * self.orbitRate * dt,
                    (self.my - mpos.getY()) * self.orbitRate * dt)

            else:
                # If the camera isn't in orbiting mode, we check to see if the mouse is close enough to the edge of the screen to start panning

                moveY = False
                moveX = False
                # these two booleans are used to denote if the camera needs to pan. X and Y refer to the mouse position that causes the
                # panning. X is the left or right edge of the screen, Y is the top or bottom.

                if self.my > (1 - self.panZoneSize):
                    angleradiansX1 = self.showbase.camera.getH() * (math.pi /
                                                                    180.0)
                    panRate1 = (1 - self.my - self.panZoneSize) * (
                        self.camDist / self.panRateDivisor)
                    moveY = True
                if self.my < (-1 + self.panZoneSize):
                    angleradiansX1 = self.showbase.camera.getH() * (
                        math.pi / 180.0) + math.pi
                    panRate1 = (1 + self.my - self.panZoneSize) * (
                        self.camDist / self.panRateDivisor)
                    moveY = True
                if self.mx > (1 - self.panZoneSize):
                    angleradiansX2 = self.showbase.camera.getH() * (
                        math.pi / 180.0) + math.pi * 0.5
                    panRate2 = (1 - self.mx - self.panZoneSize) * (
                        self.camDist / self.panRateDivisor)
                    moveX = True
                if self.mx < (-1 + self.panZoneSize):
                    angleradiansX2 = self.showbase.camera.getH() * (
                        math.pi / 180.0) - math.pi * 0.5
                    panRate2 = (1 + self.mx - self.panZoneSize) * (
                        self.camDist / self.panRateDivisor)
                    moveX = True
                # These four blocks check to see if the mouse cursor is close enough to the edge of the screen to start panning and then
                # perform part of the math necessary to find the new camera position. Once again, the math is a bit above my head, so
                # I can't properly explain it. These blocks also set the move booleans to true so that the next lines will move the camera.

                # If up or down keys are pressed
                if self.keys["cam-up"] ^ self.keys["cam-down"]:
                    moveY = True
                    panRate1 = self.keyPanRate
                    # Update warlock position on z plane
                    if self.keys["cam-up"]:
                        angleradiansX1 = self.showbase.camera.getH() * (
                            math.pi / 180.0) + math.pi
                    if self.keys["cam-down"]:
                        angleradiansX1 = self.showbase.camera.getH() * (
                            math.pi / 180.0)

                # If left or right keys are pressed
                if self.keys["cam-left"] ^ self.keys["cam-right"]:
                    moveX = True
                    panRate2 = self.keyPanRate
                    # Update warlock position on x plane
                    if self.keys["cam-left"]:
                        angleradiansX2 = self.showbase.camera.getH() * (
                            math.pi / 180.0) + math.pi * 0.5
                    if self.keys["cam-right"]:
                        angleradiansX2 = self.showbase.camera.getH() * (
                            math.pi / 180.0) - math.pi * 0.5

                if moveY:
                    tempX = self.target.getX(
                    ) + math.sin(angleradiansX1) * panRate1 * dt * 50
                    tempX = self.clamp(tempX, self.panLimitsX.getX(),
                                       self.panLimitsX.getY())
                    self.target.setX(tempX)
                    tempY = self.target.getY(
                    ) - math.cos(angleradiansX1) * panRate1 * dt * 50
                    tempY = self.clamp(tempY, self.panLimitsY.getX(),
                                       self.panLimitsY.getY())
                    self.target.setY(tempY)
                    self.turnCameraAroundPoint(0, 0)
                if moveX:
                    tempX = self.target.getX(
                    ) - math.sin(angleradiansX2) * panRate2 * dt * 50
                    tempX = self.clamp(tempX, self.panLimitsX.getX(),
                                       self.panLimitsX.getY())
                    self.target.setX(tempX)
                    tempY = self.target.getY(
                    ) + math.cos(angleradiansX2) * panRate2 * dt * 50
                    tempY = self.clamp(tempY, self.panLimitsY.getX(),
                                       self.panLimitsY.getY())
                    self.target.setY(tempY)
                    self.turnCameraAroundPoint(0, 0)
                # These two blocks finalize the math necessary to find the new camera position and apply the transformation to the
                # camera's TARGET. Then turnCameraAroundPoint is called with 0s for rotation, and it resets the camera position based
                # on the position of the target. The x and y values are clamped to the pan limits before they are applied.
                #print(self.target)
                self.mx = mpos.getX()
                self.my = mpos.getY()
                # The old mouse positions are updated to the current mouse position as the final step.

    # Finds 3d world point on the z = 0 plane for destination/target
    def getMouse3D(self):
        # make sure process has the mouse to not cause error
        if self.showbase.mouseWatcherNode.hasMouse():
            # get screen coordinates of mouse
            mpos = self.showbase.mouseWatcherNode.getMouse()
            pos3d = Point3()
            nearPoint = Point3()
            farPoint = Point3()
            # find vector of cameras facing from mouse screen coordinates and get near and far points on frustrum
            self.showbase.camLens.extrude(mpos, nearPoint, farPoint)
            # check for intersection with z = 0 plane
            if self.plane.intersectsLine(
                    pos3d,
                    self.showbase.render.getRelativePoint(
                        self.showbase.camera, nearPoint),
                    self.showbase.render.getRelativePoint(
                        self.showbase.camera, farPoint)):
                # return this position if exists
                return pos3d
        # or return (-1, -1, -1)
        return Vec3(-1, -1, -1)
Beispiel #4
0
class CameraHandler(DirectObject.DirectObject):
	def __init__(self, showbase): 
		self.showbase = showbase
		self.showbase.disableMouse()
		# This disables the default mouse based camera control used by panda. This default control is awkward, and won't be used. 

		self.showbase.camera.setPos(0, -150, 200)
		self.showbase.camera.lookAt(0, 0, 0)
		# Gives the camera an initial position and rotation. 

		self.mx, self.my = 0, 0
		# Sets up variables for storing the mouse coordinates 

		self.orbiting = False
		# A boolean variable for specifying whether the camera is in orbiting mode. Orbiting mode refers to when the camera is being moved 
		# because the user is holding down the right mouse button. 

		self.target = Vec3()
		# sets up a vector variable for the camera's target. The target will be the coordinates that the camera is currently focusing on. 

		self.camDist = 150
		# A variable that will determine how far the camera is from it's target focus 

		self.panRateDivisor = 10
		# This variable is used as a divisor when calculating how far to move the camera when panning. Higher numbers will yield slower panning 
		# and lower numbers will yield faster panning. This must not be set to 0. 

		self.panZoneSize = .1
		# This variable controls how close the mouse cursor needs to be to the edge of the screen to start panning the camera. It must be less than 1, 
		# and I recommend keeping it less than .2 

		self.panLimitsX = Vec2(-1000, 1000) 
		self.panLimitsY = Vec2(-1000, 1000) 
		# These two vairables will serve as limits for how far the camera can pan, so you don't scroll away from the map.

		self.maxZoomOut = 500
		self.maxZoomIn = 25
		#These two variables set the max distance a person can zoom in or out
		
		self.orbitRate = 75
		# This is the rate of speed that the camera will rotate when middle mouse is pressed and mouse moved
		# recommended rate 50-100

		self.setTarget(0, 0, 0) 
		# calls the setTarget function to set the current target position to the origin. 

		self.turnCameraAroundPoint(0, 0) 
		# calls the turnCameraAroundPoint function with a turn amount of 0 to set the camera position based on the target and camera distance 

		self.accept("mouse2", self.startOrbit) 
		# sets up the camrea handler to accept a right mouse click and start the "drag" mode. 

		self.accept("mouse2-up", self.stopOrbit) 
		# sets up the camrea handler to understand when the right mouse button has been released, and ends the "drag" mode when 
		# the release is detected.
		
		self.storeX = 0
		self.storeY = 0
		# for storing of the x and y for the orbit

		# The next pair of lines use lambda, which creates an on-the-spot one-shot function. 

		self.accept("wheel_up", self.zoomIn)
		# sets up the camera handler to detet when the mouse wheel is rolled upwards and uses a lambda function to call the 
		# adjustCamDist function  with the argument 0.9 

		self.accept("wheel_down", self.zoomOut)
		# sets up the camera handler to detet when the mouse wheel is rolled upwards and uses a lambda function to call the 
		# adjustCamDist function  with the argument 1.1 
		
		# Keys array (down if 1, up if 0)
		self.keys = { "cam-left": 0, "cam-right": 0, "cam-up": 0, "cam-down": 0 }
		
		# Using Arrow Keys
		self.accept("arrow_left", self.setValue, [self.keys, "cam-left", 1])
		self.accept("arrow_right", self.setValue, [self.keys, "cam-right", 1])
		self.accept("arrow_up", self.setValue, [self.keys, "cam-up", 1])
		self.accept("arrow_down", self.setValue, [self.keys, "cam-down", 1])
		self.accept("arrow_left-up", self.setValue, [self.keys, "cam-left", 0])
		self.accept("arrow_right-up", self.setValue, [self.keys, "cam-right", 0])
		self.accept("arrow_up-up", self.setValue, [self.keys, "cam-up", 0])
		self.accept("arrow_down-up", self.setValue, [self.keys, "cam-down", 0])
		
		self.keyPanRate = 1.5
		# pan rate for when user presses the arrow keys
		
		# set up plane for checking collision with for mouse-3d world
		self.plane = Plane(Vec3(0, 0, 1), Point3(0, 0, 0))

	def destroy(self):
		self.ignoreAll()

	def setValue(self, array, key, value):
		array[key] = value

	def clamp(self, val, minVal, maxVal):
		val = min(max(val, minVal), maxVal)
		return val

	def zoomOut(self):
		if self.camDist <= self.maxZoomOut:
			self.adjustCamDist(1.1)
		return True

	def zoomIn(self):
		if self.camDist >= self.maxZoomIn:
			self.adjustCamDist(0.9)
		return True

	def turnCameraAroundPoint(self, deltaX, deltaY): 
		# This function performs two important tasks. First, it is used for the camera orbital movement that occurs when the 
		# right mouse button is held down. It is also called with 0s for the rotation inputs to reposition the camera during the 
		# panning and zooming movements. 
		# The delta inputs represent the change in rotation of the camera, which is also used to determine how far the camera 
		# actually moves along the orbit. 

		newCamHpr = Vec3() 
		newCamPos = Vec3() 
		# Creates temporary containers for the new rotation and position values of the camera. 

		camHpr = self.showbase.camera.getHpr() 
		# Creates a container for the current HPR of the camera and stores those values. 

		newCamHpr.setX(camHpr.getX() + deltaX) 
		newCamHpr.setY(self.clamp(camHpr.getY() - deltaY, -85, -10)) 
		newCamHpr.setZ(camHpr.getZ()) 
		# Adjusts the newCamHpr values according to the inputs given to the function. The Y value is clamped to prevent 
		# the camera from orbiting beneath the ground plane and to prevent it from reaching the apex of the orbit, which 
		# can cause a disturbing fast-rotation glitch. 

		self.showbase.camera.setHpr(newCamHpr) 
		# Sets the camera's rotation to the new values. 

		angleradiansX = newCamHpr.getX() * (math.pi / 180.0) 
		angleradiansY = newCamHpr.getY() * (math.pi / 180.0) 
		# Generates values to be used in the math that will calculate the new position of the camera. 

		newCamPos.setX(self.camDist * math.sin(angleradiansX) * math.cos(angleradiansY) + self.target.getX())
		newCamPos.setY(-self.camDist * math.cos(angleradiansX) * math.cos(angleradiansY) + self.target.getY()) 
		newCamPos.setZ(-self.camDist * math.sin(angleradiansY) + self.target.getZ()) 
		self.showbase.camera.setPos(newCamPos.getX(), newCamPos.getY(), newCamPos.getZ()) 
		# Performs the actual math to calculate the camera's new position and sets the camera to that position. 
		#Unfortunately, this math is over my head, so I can't fully explain it. 

		self.showbase.camera.lookAt(self.target.getX(), self.target.getY(), self.target.getZ()) 
		# Points the camera at the target location.
		
	def getTarget(self):
		return self.target
		# returns the cur

	def setTarget(self, x, y, z):
		#This function is used to give the camera a new target position. 
		x = self.clamp(x, self.panLimitsX.getX(), self.panLimitsX.getY()) 
		self.target.setX(x) 
		y = self.clamp(y, self.panLimitsY.getX(), self.panLimitsY.getY()) 
		self.target.setY(y) 
		self.target.setZ(z) 
		# Stores the new target position values in the target variable. The x and y values are clamped to the pan limits. 

	def setPanLimits(self, xMin, xMax, yMin, yMax): 
		# This function is used to set the limitations of the panning movement. 

		self.panLimitsX = (xMin, xMax) 
		self.panLimitsY = (yMin, yMax) 
		# Sets the inputs into the limit variables. 

	def startOrbit(self): 
		# This function puts the camera into orbiting mode. 

		# Get windows properties
		props = WindowProperties()
		# Set Hide Cursor Property
		#props.setCursorHidden(True)
		# Set properties
		self.showbase.win.requestProperties(props)
		# hide cursor

		if self.showbase.mouseWatcherNode.hasMouse(): 
			# We're going to use the mouse, so we have to make sure it's in the game window. If it's not and we try to use it, we'll get 
			# a crash error.
			mpos = self.showbase.mouseWatcherNode.getMouse()
			self.storeX = mpos.getX()
			self.storeY = mpos.getY()
			# take current cursor values
		
		self.showbase.win.movePointer(0, self.showbase.win.getXSize() / 2, self.showbase.win.getYSize() / 2)
		# set to center
		self.mx = 0
		self.my = 0
		
		self.orbiting = True
		# Sets the orbiting variable to true to designate orbiting mode as on. 

	def stopOrbit(self): 
		# This function takes the camera out of orbiting mode. 

		self.orbiting = False 
		# Sets the orbiting variable to false to designate orbiting mode as off. 
		
		self.showbase.win.movePointer(0, int((self.storeX + 1.0) / 2 * self.showbase.win.getXSize()), int(self.showbase.win.getYSize() - ((self.storeY + 1.0) / 2 * self.showbase.win.getYSize())))
		# set to taken cursor values from startOrbit
		if self.showbase.mouseWatcherNode.hasMouse():
			# We're going to use the mouse, so we have to make sure it's in the game window. If it's not and we try to use it, we'll get 
			# a crash error.
			mpos = self.showbase.mouseWatcherNode.getMouse()
			self.mx = mpos.getX()
			self.my = mpos.getY()

		# Get windows properties
		props = WindowProperties()
		# Set Hide Cursor Property
		props.setCursorHidden(False)
		# Set properties
		self.showbase.win.requestProperties(props)
		# reshow cursor

	def adjustCamDist(self, distFactor): 
		# This function increases or decreases the distance between the camera and the target position to simulate zooming in and out. 
		# The distFactor input controls the amount of camera movement. 
		# For example, inputing 0.9 will set the camera to 90% of it's previous distance. 

		self.camDist = self.camDist * distFactor 
		# Sets the new distance into self.camDist. 

		self.turnCameraAroundPoint(0, 0) 
		# Calls turnCameraAroundPoint with 0s for the rotation to reset the camera to the new position.

	def camMoveTask(self, dt):
		# This task is the camera handler's work house. It's set to be called every frame and will control both orbiting and panning the camera. 

		if self.showbase.mouseWatcherNode.hasMouse():
			# We're going to use the mouse, so we have to make sure it's in the game window. If it's not and we try to use it, we'll get 
			# a crash error.
			
			mpos = self.showbase.mouseWatcherNode.getMouse()
			# Gets the mouse position 

			if self.orbiting: 
				# Checks to see if the camera is in orbiting mode. Orbiting mode overrides panning, because it would be problematic if, while 
				# orbiting the camera the mouse came close to the screen edge and started panning the camera at the same time. 

				self.turnCameraAroundPoint((self.mx - mpos.getX()) * self.orbitRate * dt, (self.my - mpos.getY()) * self.orbitRate * dt)

			else: 
				# If the camera isn't in orbiting mode, we check to see if the mouse is close enough to the edge of the screen to start panning 

				moveY = False 
				moveX = False 
				# these two booleans are used to denote if the camera needs to pan. X and Y refer to the mouse position that causes the 
				# panning. X is the left or right edge of the screen, Y is the top or bottom. 

				if self.my > (1 - self.panZoneSize): 
					angleradiansX1 = self.showbase.camera.getH() * (math.pi / 180.0) 
					panRate1 = (1 - self.my - self.panZoneSize) * (self.camDist / self.panRateDivisor)
					moveY = True
				if self.my < (-1 + self.panZoneSize): 
					angleradiansX1 = self.showbase.camera.getH() * (math.pi / 180.0) + math.pi 
					panRate1 = (1 + self.my - self.panZoneSize) * (self.camDist / self.panRateDivisor) 
					moveY = True
				if self.mx > (1 - self.panZoneSize): 
					angleradiansX2 = self.showbase.camera.getH() * (math.pi / 180.0) + math.pi * 0.5 
					panRate2 = (1 - self.mx - self.panZoneSize) * (self.camDist / self.panRateDivisor) 
					moveX = True 
				if self.mx < (-1 + self.panZoneSize): 
					angleradiansX2 = self.showbase.camera.getH() * (math.pi / 180.0) - math.pi * 0.5 
					panRate2 = (1 + self.mx - self.panZoneSize) * (self.camDist / self.panRateDivisor) 
					moveX = True 
				# These four blocks check to see if the mouse cursor is close enough to the edge of the screen to start panning and then 
				# perform part of the math necessary to find the new camera position. Once again, the math is a bit above my head, so 
				# I can't properly explain it. These blocks also set the move booleans to true so that the next lines will move the camera. 

				# If up or down keys are pressed
				if self.keys["cam-up"] ^ self.keys["cam-down"]:
					moveY = True
					panRate1 = self.keyPanRate
					# Update warlock position on z plane
					if self.keys["cam-up"]:
						angleradiansX1 = self.showbase.camera.getH() * (math.pi / 180.0) + math.pi
					if self.keys["cam-down"]:
						angleradiansX1 = self.showbase.camera.getH() * (math.pi / 180.0)
				
				# If left or right keys are pressed
				if self.keys["cam-left"] ^ self.keys["cam-right"]:
					moveX = True 
					panRate2 = self.keyPanRate
					# Update warlock position on x plane
					if self.keys["cam-left"]:
						angleradiansX2 = self.showbase.camera.getH() * (math.pi / 180.0) + math.pi * 0.5 
					if self.keys["cam-right"]:
						angleradiansX2 = self.showbase.camera.getH() * (math.pi / 180.0) - math.pi * 0.5 
				
				if moveY:
					tempX = self.target.getX() + math.sin(angleradiansX1) * panRate1 * dt * 50
					tempX = self.clamp(tempX, self.panLimitsX.getX(), self.panLimitsX.getY()) 
					self.target.setX(tempX) 
					tempY = self.target.getY() - math.cos(angleradiansX1) * panRate1 * dt * 50
					tempY = self.clamp(tempY, self.panLimitsY.getX(), self.panLimitsY.getY()) 
					self.target.setY(tempY) 
					self.turnCameraAroundPoint(0, 0) 
				if moveX:
					tempX = self.target.getX()-math.sin(angleradiansX2) * panRate2 * dt * 50
					tempX = self.clamp(tempX, self.panLimitsX.getX(), self.panLimitsX.getY()) 
					self.target.setX(tempX) 
					tempY = self.target.getY() + math.cos(angleradiansX2) * panRate2 * dt * 50
					tempY = self.clamp(tempY, self.panLimitsY.getX(), self.panLimitsY.getY()) 
					self.target.setY(tempY) 
					self.turnCameraAroundPoint(0, 0) 
				# These two blocks finalize the math necessary to find the new camera position and apply the transformation to the 
				# camera's TARGET. Then turnCameraAroundPoint is called with 0s for rotation, and it resets the camera position based 
				# on the position of the target. The x and y values are clamped to the pan limits before they are applied. 
				#print(self.target)
				self.mx = mpos.getX()
				self.my = mpos.getY()
				# The old mouse positions are updated to the current mouse position as the final step. 
	
	# Finds 3d world point on the z = 0 plane for destination/target
	def getMouse3D(self):
		# make sure process has the mouse to not cause error
		if self.showbase.mouseWatcherNode.hasMouse():
			# get screen coordinates of mouse
			mpos = self.showbase.mouseWatcherNode.getMouse()
			pos3d = Point3()
			nearPoint = Point3()
			farPoint = Point3()
			# find vector of cameras facing from mouse screen coordinates and get near and far points on frustrum
			self.showbase.camLens.extrude(mpos, nearPoint, farPoint)
			# check for intersection with z = 0 plane
			if self.plane.intersectsLine(pos3d,
					self.showbase.render.getRelativePoint(self.showbase.camera, nearPoint),
					self.showbase.render.getRelativePoint(self.showbase.camera, farPoint)):
				# return this position if exists
				return pos3d
		# or return (-1, -1, -1)
		return Vec3(-1, -1, -1)
Beispiel #5
0
class MoveCursor(object):
    _EDGES = 40
    _zPos = 0
    _movingUp = False
    _movingDown = False
    _color = Vec4(0.3, 0.3, 0.8, 1)
    _currentPos = Vec3(0, 0, 0)

    def __init__(self, parent, entity, foot=1):
        self.entity = entity
        self._moveRadCircleNP = NodePath("Movement Radius Node")
        self._moveLine = LineSegs()
        self._moveLineNP = NodePath("Movement Direction Line Node")
        self._moveZLine = LineSegs()
        self._moveZLineNP = NodePath("Movement Z Line Node")
        self._moveZFootNP = NodePath("Movement Z Foot Node")
        self._moveFootCircle = LineSegs()
        self._moveFootCircleNP = NodePath("Movement Foot Circle Node")
        self._attackRadCircle = LineSegs()
        self._attackRadCircleNP = NodePath("Attack Radius Node")
        self._np = NodePath("Movement Node")
        self.attackables = []
        Event.Dispatcher().register(self, 'E_Key_ZUp', self.onZChange)
        Event.Dispatcher().register(self, 'E_Key_ZDown', self.onZChange)
        Event.Dispatcher().register(self, 'E_Key_ZUp-up', self.onZChange)
        Event.Dispatcher().register(self, 'E_Key_ZDown-up', self.onZChange)
        self.aaLevel = int(GameSettings().getSetting('ANTIALIAS'))
        self.parent = parent
        self.start = Vec3(0, 0, 0)
        self.moveRad = entity.moveRad
        self.footRad = foot
        self.attackRad = entity.attackRad
        self.plane = Plane(Vec3(0, 0, 1), Point3(0, 0, 0))
        self.draw()
        self._np.reparentTo(self.parent)
        if self.aaLevel > 0:
            self._np.setAntialias(AntialiasAttrib.MLine, self.aaLevel)
        taskMgr.add(self.updateMovePos, 'Movement Indicator Update Task')

    def draw(self):
        # Setup curr mouse pos in 3d space
        posXY = self.getMouseXY()
        x = posXY.getX()
        y = posXY.getY()
        z = 0

        # Draw movement radius
        moveRadLine = LineSegs()
        moveRadLine.setThickness(1)
        moveRadLine.setColor(self._color)
        moveRadLine.moveTo(self.moveRad, 0, 0)
        for i in range(self._EDGES + 1):
            newX = (self.moveRad * math.cos((2 * math.pi / self._EDGES) * i))
            newY = (self.moveRad * math.sin((2 * math.pi / self._EDGES) * i))
            moveRadLine.drawTo(newX, newY, 0)
        moveRadGeom = moveRadLine.create()
        self._moveRadCircleNP = NodePath(moveRadGeom)
        self._moveRadCircleNP.reparentTo(self._np)

        # Draw movement foot circle
        self._moveFootCircle.setThickness(1)
        self._moveFootCircle.setColor(self._color)
        self._moveFootCircle.moveTo(self.footRad, 0, 0)
        for i in range(self._EDGES):
            newX = (self.footRad * math.cos((2 * math.pi / self._EDGES) * i))
            newY = (self.footRad * math.sin((2 * math.pi / self._EDGES) * i))
            self._moveFootCircle.drawTo(newX, newY, 0)
        self._moveFootCircle.drawTo(self.footRad, 0, 0)
        moveFootCircleGeom = self._moveFootCircle.create()
        self._moveFootCircleNP = NodePath(moveFootCircleGeom)
        self._moveFootCircleNP.reparentTo(self._np)

        # Draw movement direction line
        self._moveLine.setThickness(1)
        self._moveLine.setColor(self._color)
        self._moveLine.moveTo(0, 0, 0)
        self._moveLine.drawTo(x, y, z)
        moveLine = self._moveLine.create()
        self._moveLineNP = NodePath(moveLine)
        self._moveLineNP.reparentTo(self._np)

        # Draw Z line
        self._moveZLine.setThickness(1)
        self._moveZLine.setColor(self._color)
        self._moveZLine.moveTo(self.start)
        self._moveZLine.drawTo(x, y, z)
        moveZLine = self._moveZLine.create()
        self._moveZLineNP = NodePath(moveZLine)
        self._moveZLineNP.reparentTo(self._np)

        # Draw Attack Radius
        self._attackRadCircle.setThickness(1)
        self._attackRadCircle.setColor(0.8, 0.0, 0.0, 1)
        self._attackRadCircle.moveTo(self.attackRad, 0, 0)
        for i in range(self._EDGES + 1):
            newX = (self.attackRad * math.cos((2 * math.pi / self._EDGES) * i))
            newY = (self.attackRad * math.sin((2 * math.pi / self._EDGES) * i))
            self._attackRadCircle.drawTo(newX, newY, 0)
        attackRadCircleGeom = self._attackRadCircle.create()
        self._attackRadCircleNP = NodePath(attackRadCircleGeom)
        self._attackRadCircleNP.reparentTo(self._np)

    def updateMovePos(self, Task):
        # endPos must be transformed in the the coord sys of the model
        m_pos = self.getMouseXY()
        if m_pos is not None:
            # Transform current mouse pos
            endPos = self.parent.getRelativePoint(render, m_pos)

            # Adjust Z coord if needed
            if self._movingUp:
                self._zPos += 0.1
            elif self._movingDown:
                self._zPos -= 0.1
            endPos.setZ(self._zPos)

            # Check if we're trying to move too far, if not update pos
            dist = math.sqrt(endPos.getX()**2 + endPos.getY()**2 + 2 *
                             (endPos.getZ()**2))
            if dist <= self.moveRad:
                self._moveLine.setVertex(1, endPos)
                self._moveFootCircleNP.setPos(endPos)
                self._moveZLine.setVertex(
                    0, Point3(endPos.getX(), endPos.getY(), 0))
                self._moveZLine.setVertex(1, endPos)
                self._attackRadCircleNP.setPos(endPos)
                self._currentPos = render.getRelativePoint(self._np, endPos)

                # Check for attackable ships in range of current pos
                attackables = Entity.EntityManager().getEntitiesWithin(
                    self._currentPos, self.attackRad)
                # Unhighlight ships no longer in range
                for e in self.attackables:
                    if e not in attackables and isinstance(
                            e, Entity.EntityShip):
                        e.representation.unsetAttackable()
                # Highlight ships in range
                for e in attackables:
                    if isinstance(
                            e, Entity.EntityShip
                    ) and e != self.entity and e.owner != self.entity.owner:
                        e.representation.setAttackable()
                self.attackables = attackables
        return Task.cont

    def onZChange(self, event):
        if event.type == 'E_Key_ZUp':
            self._movingDown = False
            self._movingUp = True
        if event.type == 'E_Key_ZDown':
            self._movingUp = False
            self._movingDown = True
        if event.type == 'E_Key_ZUp-up':
            self._movingUp = False
            self._movingDown = False
        if event.type == 'E_Key_ZDown-up':
            self._movingUp = False
            self._movingDown = False

    def getMouseXY(self):
        # NOTE - this returns the mouse pos in the ships coord sys
        if base.mouseWatcherNode.hasMouse():
            mpos = base.mouseWatcherNode.getMouse()
            pos3d = Point3()
            nearPoint = Point3()
            farPoint = Point3()
            base.camLens.extrude(mpos, nearPoint, farPoint)
            if self.plane.intersectsLine(
                    pos3d, render.getRelativePoint(camera, nearPoint),
                    render.getRelativePoint(camera, farPoint)):
                #print("Mouse ray intersects ground plane at " + str(pos3d))
                return pos3d

    def getPosition(self):
        return self._currentPos

    def removeNode(self):
        taskMgr.remove('Movement Indicator Update Task')
        for e in self.attackables:
            if isinstance(e, Entity.EntityShip):
                e.representation.unsetAttackable()
        self._moveRadCircleNP.removeNode()
        self._moveLineNP.removeNode()
        self._moveZLineNP.removeNode()
        self._moveZFootNP.removeNode()
        self._moveFootCircleNP.removeNode()
        self._attackRadCircleNP.removeNode()
        self._np.removeNode()

    def __del__(self):
        # TODO - This isn't calling self.removeNode() correctly
        self._np.removeNode()
Beispiel #6
0
class MoveCursor(object):
	_EDGES = 40
	_zPos             = 0
	_movingUp         = False
	_movingDown       = False
	_color = Vec4(0.3, 0.3, 0.8, 1)
	_currentPos = Vec3(0, 0, 0)
	
	def __init__(self, parent, entity, foot=1):
		self.entity = entity
		self._moveRadCircleNP  = NodePath("Movement Radius Node")
		self._moveLine         = LineSegs()
		self._moveLineNP       = NodePath("Movement Direction Line Node")
		self._moveZLine        = LineSegs()
		self._moveZLineNP      = NodePath("Movement Z Line Node")
		self._moveZFootNP      = NodePath("Movement Z Foot Node")
		self._moveFootCircle   = LineSegs()
		self._moveFootCircleNP = NodePath("Movement Foot Circle Node")
		self._attackRadCircle  = LineSegs()
		self._attackRadCircleNP= NodePath("Attack Radius Node")
		self._np               = NodePath("Movement Node")
		self.attackables       = []
		Event.Dispatcher().register(self, 'E_Key_ZUp', self.onZChange)
		Event.Dispatcher().register(self, 'E_Key_ZDown', self.onZChange)
		Event.Dispatcher().register(self, 'E_Key_ZUp-up', self.onZChange)
		Event.Dispatcher().register(self, 'E_Key_ZDown-up', self.onZChange)
		self.aaLevel= int(GameSettings().getSetting('ANTIALIAS'))
		self.parent = parent
		self.start  = Vec3(0, 0, 0)
		self.moveRad = entity.moveRad
		self.footRad = foot
		self.attackRad = entity.attackRad
		self.plane = Plane(Vec3(0, 0, 1), Point3(0, 0, 0))
		self.draw()
		self._np.reparentTo(self.parent)
		if self.aaLevel > 0:
			self._np.setAntialias(AntialiasAttrib.MLine, self.aaLevel)
		taskMgr.add(self.updateMovePos, 'Movement Indicator Update Task')
		
	def draw(self):
		# Setup curr mouse pos in 3d space
		posXY = self.getMouseXY()
		x = posXY.getX()
		y = posXY.getY()
		z = 0
		
		# Draw movement radius
		moveRadLine = LineSegs()
		moveRadLine.setThickness(1)
		moveRadLine.setColor(self._color)
		moveRadLine.moveTo(self.moveRad, 0, 0)
		for i in range(self._EDGES + 1):
			newX = (self.moveRad * math.cos((2*math.pi/self._EDGES)*i))
			newY = (self.moveRad * math.sin((2*math.pi/self._EDGES)*i))
			moveRadLine.drawTo(newX, newY, 0)
		moveRadGeom = moveRadLine.create()
		self._moveRadCircleNP = NodePath(moveRadGeom)
		self._moveRadCircleNP.reparentTo(self._np)
		
		# Draw movement foot circle
		self._moveFootCircle.setThickness(1)
		self._moveFootCircle.setColor(self._color)
		self._moveFootCircle.moveTo(self.footRad, 0, 0)
		for i in range(self._EDGES):
			newX = (self.footRad * math.cos((2*math.pi/self._EDGES)*i))
			newY = (self.footRad * math.sin((2*math.pi/self._EDGES)*i))
			self._moveFootCircle.drawTo(newX, newY, 0)
		self._moveFootCircle.drawTo(self.footRad, 0, 0)
		moveFootCircleGeom = self._moveFootCircle.create()
		self._moveFootCircleNP = NodePath(moveFootCircleGeom)
		self._moveFootCircleNP.reparentTo(self._np)
		
		# Draw movement direction line
		self._moveLine.setThickness(1)
		self._moveLine.setColor(self._color)
		self._moveLine.moveTo(0, 0, 0)
		self._moveLine.drawTo(x, y, z) 
		moveLine = self._moveLine.create()
		self._moveLineNP = NodePath(moveLine)
		self._moveLineNP.reparentTo(self._np)
		
		# Draw Z line
		self._moveZLine.setThickness(1)
		self._moveZLine.setColor(self._color)
		self._moveZLine.moveTo(self.start)
		self._moveZLine.drawTo(x, y, z) 
		moveZLine = self._moveZLine.create()
		self._moveZLineNP = NodePath(moveZLine)
		self._moveZLineNP.reparentTo(self._np)
		
		# Draw Attack Radius
		self._attackRadCircle.setThickness(1)
		self._attackRadCircle.setColor(0.8, 0.0, 0.0, 1)
		self._attackRadCircle.moveTo(self.attackRad, 0, 0)
		for i in range(self._EDGES + 1):
			newX = (self.attackRad * math.cos((2*math.pi/self._EDGES)*i))
			newY = (self.attackRad * math.sin((2*math.pi/self._EDGES)*i))
			self._attackRadCircle.drawTo(newX, newY, 0)
		attackRadCircleGeom = self._attackRadCircle.create()
		self._attackRadCircleNP = NodePath(attackRadCircleGeom)
		self._attackRadCircleNP.reparentTo(self._np)
		
	def updateMovePos(self, Task):
		# endPos must be transformed in the the coord sys of the model
		m_pos = self.getMouseXY()
		if m_pos is not None:
			# Transform current mouse pos
			endPos = self.parent.getRelativePoint(render, m_pos)
			
			# Adjust Z coord if needed
			if self._movingUp:
				self._zPos += 0.1
			elif self._movingDown:
				self._zPos -= 0.1
			endPos.setZ(self._zPos)
			
			# Check if we're trying to move too far, if not update pos
			dist = math.sqrt(endPos.getX()**2 + endPos.getY()**2 + 2*(endPos.getZ()**2))
			if dist <= self.moveRad:
				self._moveLine.setVertex(1, endPos)
				self._moveFootCircleNP.setPos(endPos)
				self._moveZLine.setVertex(0, Point3(endPos.getX(), endPos.getY(), 0))
				self._moveZLine.setVertex(1, endPos)
				self._attackRadCircleNP.setPos(endPos)
				self._currentPos = render.getRelativePoint(self._np, endPos)
				
				# Check for attackable ships in range of current pos
				attackables = Entity.EntityManager().getEntitiesWithin(self._currentPos, self.attackRad)
				# Unhighlight ships no longer in range
				for e in self.attackables:
					if e not in attackables and isinstance(e, Entity.EntityShip):
						e.representation.unsetAttackable()
				# Highlight ships in range
				for e in attackables:
					if isinstance(e, Entity.EntityShip) and e != self.entity and e.owner != self.entity.owner:
						e.representation.setAttackable()
				self.attackables = attackables
		return Task.cont
		
	def onZChange(self, event):
		if event.type == 'E_Key_ZUp':
			self._movingDown = False
			self._movingUp = True
		if event.type == 'E_Key_ZDown':
			self._movingUp = False
			self._movingDown = True
		if event.type == 'E_Key_ZUp-up':
			self._movingUp = False
			self._movingDown = False
		if event.type == 'E_Key_ZDown-up':	
			self._movingUp = False
			self._movingDown = False
		
	def getMouseXY(self):
		# NOTE - this returns the mouse pos in the ships coord sys
		if base.mouseWatcherNode.hasMouse():
			mpos = base.mouseWatcherNode.getMouse()
			pos3d = Point3()
			nearPoint = Point3()
			farPoint = Point3()
			base.camLens.extrude(mpos, nearPoint, farPoint)
			if self.plane.intersectsLine(pos3d,
				render.getRelativePoint(camera, nearPoint),
				render.getRelativePoint(camera, farPoint)): 
					#print("Mouse ray intersects ground plane at " + str(pos3d))
					return pos3d
		
	def getPosition(self):
		return self._currentPos
		
	def removeNode(self):
		taskMgr.remove('Movement Indicator Update Task')
		for e in self.attackables:
			if isinstance(e, Entity.EntityShip):
				e.representation.unsetAttackable()
		self._moveRadCircleNP.removeNode()
		self._moveLineNP.removeNode()
		self._moveZLineNP.removeNode()
		self._moveZFootNP.removeNode()
		self._moveFootCircleNP.removeNode()
		self._attackRadCircleNP.removeNode()
		self._np.removeNode()
		
	def __del__(self):
		# TODO - This isn't calling self.removeNode() correctly
		self._np.removeNode()