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 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)