Source code for interactables

import sys,os

sys.path.append("../CommonFunctions")
#JH: Importing level window caused a circlular import
#from levelWindow import *
from Graphics import *
from math import *
from actors import SpriteActor, BaseActor
from makeShapeFunctions import makeColCircle, makeColRec

#used mostly to grabbing stage specific images
LEVEL_PATH = os.path.dirname(os.path.realpath(__file__))+"/"

[docs]class EventPad(SpriteActor): """ A non-obstructing physics object that triggers an event when touched. The EventPad covers a rectangular area of custom size. """ def __init__( self, start, width, height, collisionAction=None, collision=None, avatar=None, separation=None, use=None, interact=None, scale=1, debug=False): """ Constructs an EventPad object. Args: start: The position (center) of the pad. width: The width of the pad. height: The height of the pad. collisionAction: :func:`.callbackFunction` called when the event pad is hit. collision: :func:`.onCollision` callback called when the event pad is hit by anything. avatar: filename or :class:`.Shape` object used a the visual representation of the pad. separation: :func:`.onSeparation` callback called when something leaves the event pad. use: Function called when this pad is used after being picked up. interact: Function called when the player calls use when on top of this pad. scale: The scale of the visual representation of the pad. debug: If True, shows the hitbox of the pad. """ SpriteActor.__init__(self, position=start, base=avatar, shape=makeColRec(width,height), collision=collision, separation=separation, interact=interact, scale=scale, debug=debug) self.use=use self.done=False #turn off the error.png if not avatar and not debug: a=self.getAvatar() a.visible=False self.collisionAction=collisionAction # have to overwrite the draw function in order to add collideActionLauncher # to list of collision functions
[docs] def draw(self, levelWindow): """ Adds this object to the level window, and thus the world. Args: levelWindow: A :class:`.LevelWindow` object. """ #Set level window self.levelWindow = levelWindow #Draw shape if self.shape: self._drawShape(self.shape, self.levelWindow) #Install collision callbacks if self.collision: self.installCollision(self.collision, self.shape) if self.separation: self.installSeparation(self.separation, self.shape) if self.canInteract: self.installCollision(self.levelWindow.interactCollide, self.shape) self.installSeparation(self.levelWindow.interactSeparate, self.shape) if self.canPickup: self.installCollision(self.levelWindow.pickupCollide, self.shape) self.installSeparation(self.levelWindow.pickupSeparate, self.shape) if self.collisionAction: self.installCollision(self.collideActionLauncher, self.shape)
[docs] def collideActionLauncher(self, myfixture, otherfixture, contact): """ The default :func:`.onCollision` callback for the event pad. Calls the collisionAction, or all function in collisionAction is collisionAction is a list or tuple. Args: myFixture: The :class:`.Fixture` of the object on which this collision function is installed. otherFixture: The :class:`.Fixture` of the object colliding with the object on which this collision function was installed. contact: The :class:`.Contact` object associated with this collision. """ #Don't count the collision with the avatar #if otherfixture.Body.UserData != base: if self.collisionAction and not self.done: #print(otherfixture.Body.UserData) #print(self.base) try: self.done=True if isinstance(self.collisionAction,list) or isinstance(self.collisionAction,tuple): for a in self.collisionAction: a() else: self.collisionAction() except: print(traceback.format_exc()) return True
''' def onInteract(self,items=None): if self.use: if items!=None: self.use(items) else: self.use() '''
[docs]class Platform(SpriteActor): """ A circular platform for attaching collision or separation callbacks. """ def __init__(self, position, radius, picture, collision=None, separation=None, debug=False): """ Constructs a Platform object. Args: position: The position (center) of the pad. radius: The radius of the platform. picture: String or :class:`.Shape` used a the visual representation of the platform. collision: :func:`.onCollision` collision called when the event pad is hit by anything. separation: :func:`.onSeparation` callback called when something leaves the event pad. debug: If True, shows the hitbox of the pad. """ SpriteActor.__init__(self, position,picture,makeColCircle(radius), collision=collision,separation=separation,debug=debug)
[docs]class Lantern(SpriteActor): """ A latern object that reveals the fog-of-war in a limited area around the player. """ def __init__(self, start,working,scale=1,debug=False,onPickup=None, use=None): """ Constructs a Latern object. Args: start: The position of the lantern. working: If False, this lantern doesn't actually work. scale: The scale of the visual representation of the lantern. debug: If True, shows the hitbox of the lantern. onPickup: Function called when this item is picked-up. use: Function called when thus item is used. """ SpriteActor.__init__(self, start, LEVEL_PATH+"lampUnlit.png" , makeColCircle(scale*20, "dynamic"), scale=scale, pickup=True, debug=debug,onPickup=onPickup,use=use) self.working=working #self.extraDialogue=extraDialogue if self.working: self.description = "True" else: self.description = "False" ''' def onUse(self): #self.statusBox.hide() if self.extraDialogue != None: self.levelWindow.printStatusText(self.extraDialogue) return self.working '''
[docs]class Portal(SpriteActor): """ A pair of portals that teleports robots from one end to the other. """ def __init__(self, start, end, endAngle, costume,debug=False,scale=1,useDeath=False,additionalAction=None): """ Constructs a Portal object. Args: start: The position of the portal end: The position the portal will teleport its user to. endAngle: The direction the user will be facing after passing through the portal costume: Integer indicating wich costume to use. Customes are: 0) blue portal, 1) orange portal, 2) broken portal. debug: If True, shows the hitbox of the portal. scale: The scale of the visual representation of the portal. useDeath: If True, use of the portal will kill the user, rather than teleporting them. additionalAction: :func:`.callbackFunction` called after the portal is used. """ files = [LEVEL_PATH+"portalBlue.png", LEVEL_PATH+"portalOrange.png", LEVEL_PATH+"portalBroken.png"] SpriteActor.__init__(self, start, files , makeColCircle(35, "dynamic"), debug=debug,scale=scale) self.description = "portal" self.changeCostume(costume) self.end=end self.endAngle=endAngle self.useDeath=useDeath self.disabled=False self.level=getLevelObject() self.additionalAction=additionalAction self.stopShort=False
[docs] def onInteract(self, items): """ Implementation of the :func:`.actorOnInteract` callback function for the portal. Args: items: The list of items that passed to the use function when the user interacted with this Actor. """ if len(items)>0: #expects a function that returns True in #order to disable the short circuiting if hasattr(items[0],'__call__'): self.stopShort=items[0]() #if items!=None and hasattr(bypass, '__call__'): if self.useDeath: self.level.setGameOver(-1) elif not self.disabled: #self.levelWindow.addStepEvent(self.teleportStep) self.levelWindow.safeMoveBlock(self.end[0],self.end[1],self.endAngle) if self.additionalAction!=None: self.additionalAction() if not self.stopShort: self.level.stopScript()
#self.levelWindow.addStepEvent(lambda:self.teleportStep(self.endAngle,self.end)) #return True
[docs] def teleportStep(self): """ The event that will actually teleport the robot. The robot will have to be teleport in an event scheduled to the levelThread, rather than being teleported immediately, to avoid race conditions that can crash Calico. Return: Always returns False. """ angle=self.endAngle destination=self.end robot = getRobot() robot.stop() ## #if the user creates a function called shortCircuitBypass ## #and that funciton returns True than do not stop the script ## #during the teleport ## shortCircuit=True ## ## #try to retrieve the function ## bypass=self.level.getScopeVariable("shortCircuitBypass") ## #if bypass is really a function ## if hasattr(bypass, '__call__'): ## shortCircuit=not bypass() ## if not self.stopShort: self.level.stopScript() #print("stopping script") else: #print("not stopping script") pass #The contacts do not like the robot suddenly teleporting. Clear all of them (they will be recreated). del self.levelWindow.interactContactList[:] del self.levelWindow.pickupContactList[:] #del self.levelWindow.talkContactList[:] robot.setPose(destination[0], destination[1], angle) robot.frame.body.ResetDynamics() disableContactList(robot.frame.body.ContactList) return False
[docs]class SimpleMovingNPC(SpriteActor): """ Simple NPC that just moves along wayPoints. """ def __init__(self,wayPoints,filename=None,visible=True,debug=False,endAction=None): """ Constructs a SimpleMovingNPC object. wayPoints: List of waypoints the NPC will follow. fimename: String pointing to the file an image to be used for this NPC. visible: Whether the NPC will be visible or not. debug: If True, shows the hitbox of the NPC. endAction: :func:`.callbackFunction` called when the NPC is done moving. """ if filename==None: SpriteActor.__init__(self,wayPoints[0],Rectangle((0,0),(50,50),visible=visible,debug=debug,obstructs=False,bodyType="dynamic")) else: SpriteActor.__init__(self,wayPoints[0],filename,visible=visible,debug=debug,obstructs=False,bodyType="dynamic") self.level=getLevelObject() self.wayPoints=wayPoints self.endAction=endAction self.run=False self.atEnd=False self.endIndex=1 self.speed=25 self.start=self.wayPoints[0] self.end=self.wayPoints[1] #initial distance and direction self.distance=[self.end[0]-self.start[0],self.end[1]-self.start[1]] #distance to end of x and y self.direction=norm(self.distance)
[docs] def step(self): """ Executes one step of the NPC movement. This function is supposed to be added to the levelThread to make the NPC move. The movement can be stopped or started by changing the value of the run attribute. Return: Returns False when the movement is finished, True otherwise. """ if self.run: #print(self.start) #print(self.end) #print(self.atEnd) #if gameOver then return False and stop if self.level.gameOver!=0: self.shape.body.LinearVelocity = Vector(0,0) return False #potentially pick new start and stop points if self.atEnd: if self.endIndex<len(self.wayPoints)-1: self.atEnd=False self.endIndex+=1 self.start=self.wayPoints[self.endIndex-1] self.end=self.wayPoints[self.endIndex] self.distance=[self.end[0]-self.start[0],self.end[1]-self.start[1]] #distance to end of x and y self.direction=norm(self.distance) else: if hasattr(self.endAction, '__call__'): self.endAction() elif self.endAction=="hide": self.hide() self.run=False else: if fabs(self.distance[0])<0.01: #avoid dividing by 0 if (self.getY()-self.start[1])/self.distance[1] >= 1: #print("test1") self.atEnd=True elif fabs(self.distance[1])<0.01: #avoid divinding by 0 if (self.getX()-self.start[0])/self.distance[0] >= 1: self.atEnd=True #print("test2") else: #print(self.distance) #print(self.end) #print(self.start) #print((self.start[0]-self.end[0])/self.distance[0] ) #print((self.start[1]-self.end[1])/self.distance[1] ) if (self.getY()-self.start[0])/self.distance[0] >=1 and (self.getX()-self.start[1])/self.distance[1] >=1: self.atEnd=True #print("test3") if self.atEnd: self.shape.body.LinearVelocity = Vector(0,0) else: self.shape.body.LinearVelocity = Vector(self.speed*self.direction[0], self.speed*self.direction[1]) return True
#self.sample.append(self.quickActor(350,550,IMAGE_PATH+"unknownSample1.png",pickup=True,scale=0.25,description=-1))
[docs]class Sample(SpriteActor): """ A sample object that can be compared to other samples """ def __init__(self, x, y, fileName, code=-1,scale=0.25, **kwargs): """ Constructs a Sample object. Args: x: The x coordinate of the sample. y: The y coordinate of the sample. fileName: String pointing to an image to be used as the visual representation of the sample. code: The value of this particular sample when tested. scale: The scale of the visual representation of the sample. **kwargs: See :class:`.SpriteActor` for the keyword arguments. """ SpriteActor.__init__(self, (x,y), fileName, pickup=code,scale=scale, **kwargs) self.code=code
[docs]class TestWall(SpriteActor): """ A special wall for testing robot sensors. """ def __init__(self, wayPoints, width, height, speed=10): """ Constructs a TestWall object. Args: wayPoints: List of points the wall may visit. width: The widht of the wall. height: The height of the wall. speed: The speed with which the wall moves. """ SpriteActor.__init__(self,wayPoints[0],Rectangle((0,0),(width,height),color=Color("blue"))) self.level=getLevelObject() self.speed=speed self.atEnd=False self.run=False self.wayPoints=wayPoints self.start=self.wayPoints[0] self.end=self.wayPoints[-1]
[docs] def gotoPoint(self, p1, p2): """ Sets speed and direction of this wall for moving from waypoint p1 to waypoint p2. Args: p1: Integer index of the first waypoint. p2: Integer index of the second waypoint. """ if p1 < len(self.wayPoints) and p2<len(self.wayPoints): self.start=self.wayPoints[p1] self.end=self.wayPoints[p2] self.direct=[ self.end[0]-self.start[0],self.end[1]-self.start[1] ] self.vel=norm(self.direct) self.run=True
[docs] def step(self): """ Executes one iteration of movement for the wall. This method should be scheduled to the levelThread to enable the wall to move. One scheduled, movement of the wall can be controlled with the :meth:`gotoPoint` method. Return: Always returns True. """ if self.run: #if gameOver then return False and stop if self.level.gameOver!=0: self.shape.body.LinearVelocity = Vector(0,0) self.run=False if self.atEnd: self.run=False self.atEnd=False else: if fabs(self.direct[0])<0.01: #avoid dividing by 0 if (self.getY()-self.start[1])/self.direct[1] >= 1: self.atEnd=True elif fabs(self.direct[1])<0.01: #avoid divinding by 0 if (self.getX()-self.start[0])/self.direct[0] >= 1: self.atEnd=True else: #print("compareY",(self.getY()-self.start[1])/self.distance[1]) #print("compareX",(self.getX()-self.start[0])/self.distance[0]) if (self.getX()-self.start[0])/self.direct[0] >=1 and (self.getY()-self.start[1])/self.direct[1] >=1: self.atEnd=True if self.atEnd: self.shape.body.LinearVelocity = Vector(0,0) else: self.shape.body.LinearVelocity = Vector(self.speed*self.vel[0], self.speed*self.vel[1]) return True
[docs]class TestRow(object): """ Object for controlling multiple :class:`.TestWall` and :class:`.Portal` objects. Will create a row of 5 portals: 1 entry portal and 4 exit portals. """ def __init__(self,position,safeEnd,useAction=None): """ Constructs a TestRow object. Args: position: The position og the test row. safeEnd: Position that the portal will take the robot if the robot chooses the right portal. useAction: :func:`.callbackFunction` called when one of the portals is used. """ self.portals=[] files = [LEVEL_PATH+"portalBlue.png", LEVEL_PATH+"portalOrange.png"] self.level=getLevelObject() self.debug=False self.position=position self.safeEnd=safeEnd self.redLoc=None self.walls=[] self.safeIndex=None for i in range(7): #empty spaces if i<2: self.portals.append(None) else: #Red entrance portal if i==2: self.redLoc=(self.position[0]+i*100,self.position[1]) p=Portal((self.position[0]+i*100,self.position[1]), (0,0), 0, 1,debug=self.debug,useDeath=True) #test space else: #blue portals. make them all death for now if useAction!=None: p=Portal((self.position[0]+i*100,self.position[1]), self.safeEnd, 0, 2,debug=self.debug,useDeath=True,additionalAction=useAction) else: p=Portal((self.position[0]+i*100,self.position[1]), self.safeEnd, 0, 2,debug=self.debug,useDeath=True) self.portals.append(p) self.level.addActor(p) x=self.position[0] y=self.position[1] #empty space, mainly to avoid confusion w/ IR self.walls.append(None) #location of test item self.walls.append(None) #location of entrance red portal self.walls.append(None) #leftIR==0 and rightIR==0 self.walls.append(TestWall([(x-50,y),(x+50,y)],5,100)) #leftIR==0 and rightIR==1 self.walls.append(TestWall([(x-50,y+25),(x+50,y+25)],5,50)) #leftIR==1 and rightIR==0 self.walls.append(TestWall([(x-50,y-25),(x+50,y-25)],5,50)) #leftIR==1 and rightIR==0 self.walls.append(None) for w in self.walls: if w: self.level.addActor(w) self.level.addStepEvent(w.step)
[docs] def resetWalls(self): """ Resets the walls. Return: True if wall 3 is atEnd, but walls 4 and 5 are not at end. """ #reset walls for w in self.walls: if w!=None: w.runForward=False w.runBackward=True if self.walls[3].atEnd and not self.walls[4].atEnd and not self.walls[5].atEnd: return True else: for i in range(len(self.walls)): if i==self.safeIndex and self.walls[i]!=None: self.walls[i].runForward=True self.walls[i].runBackward=False return False
[docs] def setSafePortal(self,safeIndex): """ Species which of the portals is the 'safe' portal that won't kill you. Args: safeIndex: Integer index of the safe portal. """ self.safeIndex=safeIndex try: #self.level.addStepEvent(self.resetWalls) #set safe location of portal for i in range(len(self.portals)): if self.portals[i]!=None: if i==self.safeIndex: self.portals[i].useDeath=False self.portals[i].changeCostume(0) #self.portals[i].getAvatar().setAlpha(125) if self.walls[i]!=None: self.walls[i].gotoPoint(0,1) elif i!=2: #Red entrance portal self.portals[i].useDeath=True self.portals[i].changeCostume(2) #self.portals[i].getAvatar().setAlpha(255) if self.walls[i]!=None: self.walls[i].gotoPoint(1,0) except: print("error in setSafePortal")
[docs]class TimerDisplay(): """ Display showing a time counting down. """ def __init__(self,loc,width=75,height=50): """ Constructs a TimerDisplay object. Args: loc: The position of the timer display. width: The width of the timer display. height: The height of the timer display. """ self.level=getLevelObject() #timerDisplay self.width=width self.height=height self.level=getLevelObject() self.disp=self.level.addObstacle(Rectangle((loc[0]-self.width/2,loc[1]-self.height/2),(loc[0]+self.width/2,loc[1]+self.height/2),color=Color("white"))) self.disp.outline=Color("black") self.disp.setWidth(3) self.dispText=self.level.addObstacle(Text((self.disp.getX(),self.disp.getY()),"0:00",color=Color("black"),fontSize=18,xJustification="right")) self.run=False
[docs] def updateTimerDisplay(self,e): """ Updates the timer display. Args: e: Event causing the timer to be updated. """ try: #e is an event s=None if e !=None: s=e.ticks if s!=None: s=str(s) if len(s)==1: s="0"+s #add a decimal to the number d=s[:-1]+"."+s[-1:] self.dispText.setText(d) except: print("error") return True
[docs]class TestKeyPad(): """ Test key pad object. """ def __init__(self,loc,wayPoints=None,touchAction=None,wrongCodeAction=None,correctCodeAction=None,testType=0): """ Constructs a TestKeyPad object. Args: loc: The position of the key pad. waypoint: Waypoints for the key pad. touchAction: :func:`.callbackFunction` called when the pad is touched. wrongCodeAction: :func:`.callbackFunction` called when the wrong codes is provided to the key pad. correctCodeAction: :func:`.callbackFunction` called when the wrong codes is provided to the key pad. testType: Integer indicating the type of test. """ self.level=getLevelObject() self.robot=None self.touchAction=touchAction self.wrongCodeAction=wrongCodeAction self.correctCodeAction=correctCodeAction self.testType=testType self.corridor=[] self.keyPad=[] self.keyPadText=[] self.testObject=[] self.robotWidth=50 self.corrWidth=75 self.corrHeight=100 self.selectedCase=None self.usePad=self.level.addActor(EventPad((loc[0],loc[1]+self.corrHeight/8),self.robotWidth,self.corrHeight/4,avatar=None,scale=0.20,collision=self.touchCodePad,interact=self.checkCode,debug=True)) #create the corridor on the left and right self.corridor.append(Rectangle((loc[0]-self.robotWidth/2-self.corrWidth,loc[1]-self.corrHeight/2), (loc[0]-self.robotWidth/2,loc[1]+self.corrHeight/2))) self.corridor.append(Rectangle((loc[0]+self.robotWidth/2,loc[1]-self.corrHeight/2), (loc[0]+self.robotWidth/2+self.corrWidth,loc[1]+self.corrHeight/2))) self.keyPad.append(Rectangle((loc[0]+self.robotWidth/2,loc[1]-self.corrHeight/2), (loc[0]+self.robotWidth/2+self.corrWidth/4,loc[1]-self.corrHeight/4))) self.keyPadText.append(Text((self.keyPad[-1].getX(),self.keyPad[-1].getY()),"0",color=Color("black"))) self.keyPad.append(Rectangle((loc[0]+self.robotWidth/2+self.corrWidth/4,loc[1]-self.corrHeight/2), (loc[0]+self.robotWidth/2+self.corrWidth/2,loc[1]-self.corrHeight/4))) self.keyPadText.append(Text((self.keyPad[-1].getX(),self.keyPad[-1].getY()),"1",color=Color("black"))) self.keyPad.append(Rectangle((loc[0]+self.robotWidth/2,loc[1]-self.corrHeight/4), (loc[0]+self.robotWidth/2+self.corrWidth/4,loc[1]))) self.keyPadText.append(Text((self.keyPad[-1].getX(),self.keyPad[-1].getY()),"2",color=Color("black"))) self.keyPad.append(Rectangle((loc[0]+self.robotWidth/2+self.corrWidth/4,loc[1]-self.corrHeight/4), (loc[0]+self.robotWidth/2+self.corrWidth/2,loc[1]))) self.keyPadText.append(Text((self.keyPad[-1].getX(),self.keyPad[-1].getY()),"3",color=Color("black"))) w=self.robotWidth h=5 if self.testType==0: self.testObject.append(TestWall( [ [loc[0]-self.robotWidth/2-w/2,loc[1]-self.corrHeight/2+h],[loc[0],loc[1]-self.corrHeight/2+h] ],self.robotWidth,5)) self.testObject.append(TestWall( [ [loc[0]-self.robotWidth/2-(.75*w),loc[1]-self.corrHeight/2+h],[loc[0]-0.25*self.robotWidth,loc[1]-self.corrHeight/2+h] ],self.robotWidth/2,5)) self.testObject.append(TestWall( [ [loc[0]-self.robotWidth/2-(.25*w),loc[1]-self.corrHeight/2+h],[loc[0]+.25*self.robotWidth,loc[1]-self.corrHeight/2+h] ],self.robotWidth/2,5)) self.testObject.append(None) else: self.testObject.append(TestWall( [ [loc[0]-self.robotWidth/2-w/2,loc[1]],[loc[0],loc[1]] ],self.robotWidth,25)) self.testObject.append(TestWall( [ [loc[0]-self.robotWidth/2-(.75*w),loc[1]],[loc[0]-0.25*self.robotWidth,loc[1]] ],self.robotWidth/2,25)) self.testObject.append(TestWall( [ [loc[0]-self.robotWidth/2-(.25*w),loc[1]],[loc[0]+.25*self.robotWidth,loc[1]] ],self.robotWidth/2,25)) self.testObject.append(None) for t in self.testObject: if t !=None: self.level.addActor(t) self.level.addStepEvent(t.step) if self.testType==1: t.shape.body.IsSensor=True t.shape.tag="line" for c in self.corridor: self.level.addObstacle(c) c.setWidth(0) self.level.addObstacle(Text( (loc[0]+self.robotWidth/2+self.corrWidth/4+2,loc[1]-self.corrHeight/2+15),"KeyPad" ,color=Color("black"),fontSize=12)) for k in self.keyPad: self.level.addObstacle(k) k.fill=Color("gray") k.move(0,25) for t in self.keyPadText: self.level.addObstacle(t) t.move(0,25) self.lockedLight=self.level.addObstacle(Circle((loc[0]-self.robotWidth/2-self.corrWidth/2,loc[1]-10),5,color=Color("red"))) self.unlockedLight=self.level.addObstacle(Circle((loc[0]-self.robotWidth/2-self.corrWidth/2,loc[1]+10),5,color=Color("purple")))
[docs] def touchCodePad(self, myfixture, otherfixture, contact): """ Default :func:`.onCollision` callback called when the key pad is hit. Calls the touchAction callback if present. Args: myFixture: The :class:`.Fixture` of the object on which this collision function is installed. otherFixture: The :class:`.Fixture` of the object colliding with the object on which this collision function was installed. contact: The :class:`.Contact` object associated with this collision. """ r=getRobot() if otherfixture.Body.UserData==r.frame.body.UserData: if hasattr(self.touchAction, '__call__'): self.touchAction()
[docs] def checkCode(self, foo, code): """ Default :func:`.actorOnInteract` callback called when the user calls use when standing on the key pad. Function checks if the code intered by the user is correct. If it is, it executes the correctCodeAction. Otherwise, it will call the wrongCodeAction. Args: foo: The actor of which the use action is called. Not used by this function. code: The code passed by the user (as a list). """ if code[0]!=None: if self.selectedCase==code[0]: if hasattr(self.correctCodeAction, '__call__'): self.correctCodeAction() self.level.pauseRobot(False) self.unlockedLight.fill=Color("green") self.keyPad[code[0]].fill=Color("green") #reset this test example if self.testObject[code[0]]!=None: self.testObject[code[0]].gotoPoint(1,0) else: if hasattr(self.wrongCodeAction, '__call__'): self.wrongCodeAction() #print("Etnered wrong code.") self.keyPad[code[0]].fill=Color("red")
#self.enteredCode=items[0] #self.currentCodeText.setText(str(self.enteredCode))
[docs] def setTestCase(self,case): """ Sets the test case for the key pad. Args: case: Interger indicating the index of the case to set. """ try: for i in range(len(self.testObject)): if i==case: self.keyPad[i].fill=Color("white") self.selectedCase=case if self.testObject[case]!=None: self.testObject[case].gotoPoint(0,1) else: self.keyPad[i].fill=Color("gray") if self.testObject[i]!=None: self.testObject[i].gotoPoint(1,0) except: print("error")
[docs] def resetTestCases(self): """ Causes all test cases to go to their default positions. """ for t in self.testObject: if t != None: t.gotoPoint(1,0)
[docs]class TrollCompare(object): """ This is troll object that will return false for equality less than, and greater than """ def __init__(self): pass def __eq__(self,other): return False def __lt__ (self,other): return False def __gt__ (self,other): return False def __str__(self): return "Object that always returns false when compared to itself with ==, <, or >"
[docs]class ClippedFrame(Frame): """ Frame object that will only draw items within a certain bounding box. """ def __init__(self, x, y): """ Constructs a ClippedFrame object. Args: x: The x coordinate of the frame. y: The y coordinate of the frame. """ Frame.__init__(x, y) self.levelWindow = None self.xOff = 0 self.yOff = 0 self.width = 100 self.height = 100 self.dirty = True
[docs] def updateFromPhysics(self): """ Updates all shapes after a step of the physics simulator. Necessary, otherwise objects added to the frame won't appear in the correct location of the screen. """ for shape in self.shapes: shape.updateFromPhysics()
[docs] def render(self, context): """ Overwite of the render method. By overwriting the render method, we can restrict the drawing to a particular box. Args: context: The Cairo painting context. """ try: context.Save() context.NewPath() context.MoveTo(self.xOff, self.yOff) context.LineTo(self.xOff, self.yOff + self.height) context.LineTo(self.xOff + self.width, self.yOff + self.height) context.LineTo(self.xOff + self.width, self.yOff) context.Clip() Frame.render(self, context) context.Restore() except: print("Error with render in Clipped Frame")
[docs]class DoubleDoor(BaseActor): """ A double door object that can be opened. The double door can be drawn like a rectangle, and will then be plit in two such that it opens in the direction in which the door is longest. As such, it is impossible to make a door that is really narrow but thick with this object. """ def __init__(self, x, y, w, h, c=makeColor(200,200,200)): """ Constructs a DoubleDoor object. Args: x: The x coordinate of the door. y: The y coordinate of the door. w: The width of the door. h: The height of the door. c: The color of the door. If c is set to 1, a rusted gate sprite is drawn instead of a solid color. """ self.frame = ClippedFrame(0,0) self.frame.xOff = x self.frame.yOff = y self.frame.height = h self.avatarTop = None self.avatarBottom = None if c == 1: self.avatarTop = makePic(LEVEL_PATH + "GateRustedTop.png") self.avatarBottom = makePic(LEVEL_PATH + "GateRustedBottom.png") c=makeColor(0,0,0, 0) if w > h: self.gate1 = Rectangle((x,y), (x+w/2,y+h), fill=c) self.gate2 = Rectangle((x+w/2,y), (x+w,y+h), fill=c) self.frame.width = w # Some room for the shadow self.frame.height = h +3 if self.avatarTop: self.avatarTop.rotate(-90) self.avatarTop.draw(self.gate2) self.gate2.border=0 if self.avatarBottom: self.avatarBottom.rotate(-90) self.avatarBottom.draw(self.gate1) self.gate1.border=0 else: self.gate1 = Rectangle((x,y), (x+w,y+h/2), fill=c) self.gate2 = Rectangle((x,y+h/2), (x+w,y+h), fill=c) self.frame.width = w +3 self.frame.height = h # some room for the shadow if self.avatarTop: self.avatarTop.draw(self.gate1) self.gate1.border=0 if self.avatarBottom: self.avatarBottom.draw(self.gate2) self.gate2.border=0 self.pOpen = 0 self.gate1x = self.gate1.x self.gate1y = self.gate1.y self.gate2x = self.gate2.x self.gate2y = self.gate2.y self.w = w self.h = h self.levelWindow = None self.onOpen=None self.update()
[docs] def draw(self, levelWindow): """ Draw the door to the level window. Args: levelWidow: The :class:`.LevelWindow` to draw to. """ self.levelWindow = levelWindow self.levelWindow.addDecor(self.frame) self.gate1.draw(self.frame) self.gate2.draw(self.frame) self.gate1.addToPhysics() self.gate2.addToPhysics() self.gate1.bodyType="static" self.gate2.bodyType="static"
#self.levelWindow.addObstacle(self.gate1) #self.levelWindow.addObstacle(self.gate2)
[docs] def openEvent(self): """ Executes one iteration of opening the door. This method should be scheduled to the levelThread in order to open the door. Return: False when the door has finished opening, True otherwise. """ if self.pOpen < 1.0: self.pOpen += 0.1 if self.pOpen > 1.0: self.pOpen = 1.0 self.update() return not (self.pOpen == 1.0)
[docs] def closeEvent(self): """ Executes one iteration of closing the door. This method should be scheduled to the levelThread in order to close the door. Return: False when the door has finished closing, True otherwise. """ if self.pOpen > 0.0: self.pOpen -= 0.1 if self.pOpen < 0.0: self.pOpen = 0.0 self.update() return not (self.pOpen == 0.0)
[docs] def update(self): """ Draw the two parts of the door in the correction location depending on the value of the pOpen attribute. """ if self.w > self.h: self.gate1.moveTo(self.gate1x - self.pOpen*self.gate1.width, self.gate1y) self.gate2.moveTo(self.gate2x + self.pOpen*self.gate2.width, self.gate2y) else: self.gate1.moveTo(self.gate1x, self.gate1y - self.pOpen*self.gate1.height) self.gate2.moveTo(self.gate2x, self.gate2y + self.pOpen*self.gate2.height)
[docs] def open(self): """ Schedules the open event to the levelThread. """ self.levelWindow.addStepEvent(self.openEvent)
[docs] def close(self): """ Schedules the close event to the levelThread. """ self.levelWindow.addStepEvent(self.closeEvent)