#Standard libraries
from time import sleep,time
from math import sqrt,cos,sin,pi,copysign,radians,isnan
from random import random,randrange,choice,randint
from os.path import dirname, realpath
import os
import sys
import warnings
import traceback
import inspect
###########Calico Specific Libraries###############
#Simulation and graphic drawing
from Myro import *
from Graphics import *
#Libraries that allow access to Calico objects.
#(May eventually migrate to C# code)
import Gdk
import Cairo
##########Developer Created Libraries##############
#Files within Common Functions that facility creating of game infrastucture
if __name__ == "__main__":
sys.path.append(dirname(realpath(__file__))+"/../CommonFunctions")
sys.path.append(dirname(realpath(__file__))+"/../GameObjects")
sys.path.append(dirname(realpath(__file__))+"/../WindowObjects")
from hazards import *
from interactables import *
from events import *
from spriteSheet import SpriteSheet
#Hack to allow the access of the global calico object
from setCalico import *
#Global functions made for the user.
#Includes new robot commands like pickup() and use()
from userFunctions import *
#Helper functions for create text or buttons for panels.
from constructionFunctions import *
#TODO - Functions for smart formating of text and images.
#Will refactor into a general fancyText object. JH
from briefingFunctions import *
# Use for quickly making shapes
from makeShapeFunctions import *
#Data structure that stores user data.
from windowObjects import GameData,statObject
#Objects that will be eventually folded into C# code
from tempFrameObjects import MasterFrame,WorldFrame, LRC_Background
#Allows levelWindow to posses helper functions that create different game
#objects
from actors import *
from conversation import *
from events import *
from interactables import Platform
#Path of images used through levelWindow
IMAGE_PATH=dirname(dirname(realpath(__file__)))+"/CommonImages/"
USE_FAILED="Nothing to use()."
PICKUP_FAILED="Nothing to pickup()."
from version import VER
[docs]class HelpPageWidget():
def __init__(self,panel,point1,point2,title,buttonInfo,tag,debug=False):
self.parent=panel
#shapes are drawn to the center of the panel so they must be
#offset to the upper left corner (0,0)
oX=self.parent.width/2
oY=self.parent.height/2
#main container that is drawn to the panel,
#holds the titles, dividers, help buttons, etc
self.display=Rectangle((point1[0]-oX,point1[1]-oY),(point2[0]-oX,point2[1]-oY))
if debug:
self.display.fill=Color("red")
else:
self.display.fill.alpha=0
self.display.setWidth(0)
self.display.draw(self.parent)
self.display.tag=tag
#do not confuse the display width and height with the
#parent width and height
self.width=self.display.width
self.height=self.display.height
self.margin = 5
#add title and divider line
#note items are drawn directly to the shape
self.title=createPanelText(self.display,self.margin,0,title,c=Color("black"),fS=18)
currY=1.25*self.title.fontSize#self.title.height
self.divider1=createPanelLine(self.display,(0,currY),(self.width,currY),c=Color("black"))
#add buttons
self.numRows=len(buttonInfo)
self.maxCols=3
#for i in range(self.numRows):
# if len(buttonInfo[i])>self.maxCols:
# self.maxCols=len(buttonInfo[i])
#hard coding these dimensions for now
#self.buttonW=self.width/self.maxCols
self.buttonW=75#self.width/3
#self.buttonH=(self.height-currY)/self.numRows
self.buttonH=25#(self.height-currY)/4
#dictionary that holds all the buttons
#setButtonsVisible and setButtonsInvisible will use the
#key value to hide and show buttons.
#currently using a button's dispaly name as the key
self.buttonAndText={}
for i in range(self.numRows):
for j in range(len(buttonInfo[i])):
#if any field of buttonInfo is set to None don't add the button
if buttonInfo[i][j][0] and buttonInfo[i][j][1] and buttonInfo[i][j][2]:
[b,t]=createPanelButton(self.display,j*self.buttonW,currY+i*self.buttonH,self.buttonW,self.buttonH,
buttonInfo[i][j][0],buttonInfo[i][j][1],None,Color(buttonInfo[i][j][2]),
Color("black"),12)
#I'm saving the text object along with the button
#could simply just save the button if I wanted. RV
self.buttonAndText[t.getText()]=[b,t]
return self.display
[docs] def getClickedButton(self,x,y):
#required to register the correct click location
p=self.display.center
cX=p[0]
cY=p[1]
for key,bt in self.buttonAndText.iteritems():
# JH: The offsets should no longer be required
#print("Hit a button at ",x,y)
if bt[0].hit(x,y):
return bt[0].tag
[docs] def setButtonsInvisible(self,buttonDisplayNames=[]):
if len(buttonDisplayNames)==0:
for key,bt in self.buttonAndText.iteritems():
bt[0].visible=False
else:
for d in buttonDisplayNames:
if d in self.buttonAndText:
self.buttonAndText[d][0].visible=False
[docs] def flashButton(self):
for b in self.newlyVisible:
if b.visible:
b.visible=False
else:
b.visible=True
[docs] def setButtonsVisible(self,buttonDisplayNames=[],numFlashes=0):
if isinstance(buttonDisplayNames, str):
buttonDisplayNames=list(buttonDisplayNames)
self.newlyVisible=[]
if len(buttonDisplayNames)==0:
for key,bt in self.buttonAndText.iteritems():
bt[0].visible=True
self.newlyVisible.append(bt[0])
else:
for d in buttonDisplayNames:
if d in self.buttonAndText:
self.buttonAndText[d][0].visible=True
self.newlyVisible.append(self.buttonAndText[d][0])
if numFlashes>0:
#2 times numFlahes b/c the button with go invisible and then visible
buttonFlashEvent = RepeatedActionEvent(0.4,numFlashes*2,self.flashButton)
L=getLevelObject()
L.addStepEvent(buttonFlashEvent)
[docs]class LevelWindow(LevelObject):
"""
The level window is the parent for all Scribbler Adventure levels.
The level window is the object on which you can build a Scribbler Adventure
level. To create a level you'll need to overwrite the create level function,
and add statements to it that will build your level.
"""
def __init__(self,
levelName="levelName",
gameData=None,
parent=None,
debug=False):
"""
Creates a LevelWindow object.
Args:
levelName: The name of the level.
gameData: Data that should be provided by the parent portal window.
parent: A reference to the parent window (the calico app).
debug: Set to True to enable certain debug statements.
"""
########### Level Window Specific ##############
#: Turns on debug messages
self.debug=debug
#: Name used in panel and window
self.levelName=levelName
#: Application that launched this level
self.parent=parent
############ Viewport Dimensions ############
#: Width of the updated screen area (level + ui)
self.viewW=925
#: Heigth of the updated screen area (level + ui)
self.viewH=750
#: X origin of viewPort
self.vX=0
#: Y origin of viewPort
self.vY=0
############## User Data and Stats ################
if gameData==None:
#Todo: Create a gameData object that simply extends a dictionary, but has all of these
#default parameters in place so that to init gameData it is simply self.gameData=GameData[VER]
#currently these four lines of code are repeated in PortalWindow and ScribblerAdventureWindow
#which is bad. RV
self.gameData={}
self.gameData["ScribblerAdventure"]={}
self.gameData["ScribblerAdventure"]["logins"]=[]
self.gameData["ScribblerAdventure"]["highestStage"]=0
self.gameData["ScribblerAdventure"]["highestLevel"]=0
#mostly holds login/clicks for stages
self.gameData["ScribblerAdventure"]["stageData"]={}
#holds clicks for levels as well as game info like [score,wins,bestTime,loses,restarts]
self.gameData["ScribblerAdventure"]["levelData"]={}
#self.gameData=GameData(VER)
else:
self.gameData=gameData
#: Holds current user score for the level
self.levelScore=0
#: MaxTime is 60 seconds or one minute
self.maxTime=60
############ Game Mechanics Variables #################
#attributes that control the game dynamices
self.startTime=None
self.runTimer=False
self.gameOver=0
#used to lock the end pad so that the game over doesn't trigger
#used in self.endCollide
self.endPadLocked=False
#general contact object. Currently being used for the endPad
self.tempContact=None
# Can be toggled to false to prevent dialog from closing on movement.
self.closeDialogOnMove = True
############## Level Objects ###############
#: The conversation object that manages all conversations.
self.conversation = Conversation()
#: List for storing robots
self.robots=[]
########### Misc ##################
#attribute used to measure frame-rate, if available.
self.previousTime=0
self.avarageFps=0
self.frameCount=0
#: Need to iterate over calico.tags to access the keys
self.printTags=[]
for t in calico.tags:
self.printTags.append(t)
self.teleportState=0
############ Data Structures for User Commands #############
#: List keeping track of all the interactable object the robot is
#: currenlty in contact with to speed-up interactions
self.interactContactList = []
#: List keeping track of all the objects you can pickup that the robot
#: is currenlty in contact with to speed-up pickup.
self.pickupContactList = []
############# Events Intialization ####################
#: The event queue
self.stepEvents = []
#: The "events to add" queue to avoid concurrency issues
self.toAdd = []
#: The events that need to be triggered when the robot leaves the
#: starting pad
self.startEvents = []
#: The events that need to be triggered when the robot reaches the end
#: pad.
self.endEvents = []
#: A singular handle to the status box hide event, such that later hide
#: event can overwrite older ones.
self.statusBoxHideEvent = StaticEvent()
self.addStepEvent(self.statusBoxHideEvent)
#TODO - Fix tracking. JH
#The event to track the robots position and update the world in response
#self.trackRobotEvent = TrackRobotEvent(self, rStartX, rStartY)
#self.addStepEvent(self.trackRobotEvent)
#The event to check whether the screen has been resized
#(TODO: Should be migrated to Calico).
self.trackResizeEvent = TrackResizeEvent(self)
self.addStepEvent(self.trackResizeEvent)
#: Event queue that blocks the conversation
self.queue = EventQueue()
self.queue.sticky = True
self.addStepEvent(self.queue)
############### Panel Parameters ###############
#Need to specify the dimensions of the panels before the simulation is
#created
self.panel1H=700
self.panel1W=700
self.panel2H=50
self.panel2W=self.panel1W
self.panel3H=self.panel1H+self.panel2H
self.panel3W=225
#Panel3 items and info pages
# '''
# self.rowColors=[Color("red"),
# Color("lightblue"),
# Color("purple"),
# Color("green"),
# Color("orange")]
# self.rowTitles=["Robot Actions:",
# "Robot Sensors:",
# "Calico",
# "Programming:",
# "Misc:"]
# #the rows of tags should line up with the titles'
# #the itmes in rowTags[0] are associated with rowTitles[0]
# self.rowTags=[
# ["Move1","Move2","Wait","Pen","Beep", "Talk", "Use", "Pickup"],
# ["Stall","Line","Light","IR","Obstacle"],
# ["Shell","Output","Errors","Scripts"],
# ["variables","commands","print","functions1","functions2","indent",
# "if","while","for", "compare","lists"],
# ["Shortcuts"]]
# '''
#dictionary that stores association between a help page tag and the image that brings it up
#alternatively the filename could be a pickle of a briefing object/shape
self.pages={"talkPage":"talkPage.png","forwardPage":"move1Page.png","backwardPage":"move1Page.png",
"turnLeftPage":"move1Page.png","turnRightPage":"move1Page.png","motorsPage":"move2Page.png",
"shellPage":"shellPage.png","outputPage":"outputPage.png","errorsPage":"errorPage.png",
"shortcutsPage":"shortcutsPage.png","waitPage":"waitPage.png","usePage":"usePage.png",
"functions1Page":"functions1Page.png","commandsPage":"commandsPage.png","scriptPage":"scriptPage.png",
"irPage":"irPage.png","linePage":"linePage.png","ifPage":"ifPage.png","comparePage":"comparePage.png",
"pickupPage":"pickupPage.png","functions2Page":"functions2Page.png","variablesPage":"variablesPage.png",
"whilePage":"whilePage.png","forPage":"forPage.png","distancePage":"distancePage.png","obstaclePage":"obstaclePage.png",
"setIRPage":"setIRPage.png","beepPage":"beepPage.png","microphonePage":"microphonePage.png","lightPage":"lightPage.png",
}
for key,value in self.pages.iteritems():
self.pages[key]=IMAGE_PATH+value
#helps pages for the intro stage. triples - (displayName, tag, color)
self.IntroHelpPages=[
[("talk()","talkPage","red"),("forward()","forwardPage","red"),("backward()","backwardPage","red")],
[("turnLeft()","turnLeftPage","red"),("turnRight()","turnRightPage","red"),("motors()","motorsPage","red")],
[("Shell","shellPage","purple"),("Output","outputPage","purple"),("Errors","errorsPage","purple")],
[("Shortcuts","shortcutsPage","orange")]]
self.GauntletHelpPages=[
[("wait()","waitPage","red"),("use()","usePage","red")],
[("functions1","functions1Page","green"),("commands","commandsPage","green")],
[("Script","scriptPage","purple")]]
self.LabyrinthHelpPages=[
[("pickup()","pickupPage","red"), ("variables", "variablesPage", "green"), ("functions2","functions2Page","green")]]
self.ChamberHelpPages=[
[("getIR()","irPage","red"),("getLine()","linePage","red")],
[("if","ifPage","green"),("compare","comparePage","green")] ]
self.FixmeHelpPages=[
[("while","whilePage","green"),("for","forPage","green")]]
self.ClosedLoopHelpPages=[
[("getDistance()","distancePage","red"),("getObstacle()","obstaclePage","red"),("setIRPower()","setIRPage","red")],
[("getMicrophone()","microphonePage","red"),("beep()","beepPage","red"),("getLight()","lightPage","red")],
]
#brief screen objects
self.briefButton=[]
self.briefShapeList=[]
#entry objects
self.entryObjects=[]
#used to record current showing page
#allows toggling current page on and off
self.pageTag=None
# #these page file names should line up with the tags
# self.pageNames=[
# ["move1Page.png","move2Page.png","waitPage.png","penPage.png",
# "beepPage.png","talkPage.png","usePage.png","pickupPage.png"],
# ["stallPage.png","linePage.png","lightPage.png","irPage.png",
# "obstaclePage.png"],
# ["shellPage.png","outputPage.png","errorPage.png","scriptPage.png"],
# ["variablesPage.png","commandsPage.png","printPage.png",
# "function1Page.png","functions2Page.png","indentPage.png",
# "ifPage.png","whilePage.png","forPage.png","comparePage.png",
# "listsPage.png"],
# ["shortcutsPage.png"]]
# for i in range(len(self.pageNames)):
# for j in range(len(self.pageNames[i])):
# self.pageNames[i][j]=IMAGE_PATH+self.pageNames[i][j]
# #dictionary to holds pairing between tags and page filenames
# self.pages={}
[docs] def setup(self,runSimulator=True):
"""
Creates the window and starts the level.
Args:
runSimulator: if true, the simulator is started.
"""
#Creates the simulation as well as the master frame, which then holds
#the world and uiFrame.
self.sim=self._createSim()
self.sim.simStep = 0.03
self.speechBox = SpeechBox(self)
self.speechBox.draw(self)
# Create the panel objects and draw them to self.uiFrame which is
# created in self.createSim()
self.panel1=makeSimplePanel((self.vX,self.vY),
(self.panel1W,self.panel1H),
self.uiFrame,"panel1",
makeColor(0,0,0,0),self.panel1Click,False)
self.panel2=makeSimplePanel((self.vX,self.vY+self.panel1H),
(self.panel1W,self.panel1H+self.panel2H),
self.uiFrame,"panel2",
Color("white"),self.panel2Click)
self.panel3=makeSimplePanel((self.vX+self.panel1W,self.vY),
(self.panel1W+self.panel3W,self.panel1H+self.panel2H),
self.uiFrame,"panel3",Color("white"),self.panel3Click)
#Draw buttons, pictures, text, and shapes to the panels
self._setupPanel1(self.panel1)
self._setupPanel2(self.panel2)
self.helpWidgets=[]
self._setupPanel3(self.panel3)
#Catch all for objects added to a level. Preferred order of creation
#1. Background/Foreground/Texture
#2. Static world objects
#3. Start and end platforms
#4. Dynamic world objects including moving NPCs or hazards
#5. Create the robot
#6. Fog of War if needed
self.createLevel()
if runSimulator:
self.sim.setup()
#Catch all for actions that don't need to be done in levelWindow init or
#setup. Such as:
#-Creating and loading the brief screen
#-Setting and starting conversation with talk()
#-Pausing the robot
self.onLevelStart()
###########################
# Initial Setup Functions #
###########################
def _createSim(self,bW=1):
"""
Creates the simulation as well as the master frame, which then holds
the world and uiFrame.
Args:
bW: width of the border of the simulator.
"""
#preserving location of last window
try:
lastWindow=getWindow()
#if lastWindow is not None:
#print("Last window is ",lastWindow)
position=lastWindow.GetPosition()
width=max(lastWindow.width,self.viewW)
height=max(lastWindow.height,self.viewH)
#print("Width and height are ",width,height)
#print("Last position is ",position)
#sim.window.Move(position[0],position[1]+21)
#create simulation
sim = Simulation(self.levelName,
self.viewW,
self.viewH,
Color("white"))
sim.window.Move(position[0],position[1]+21)
sim.window.Resize(width,height)
except:
sim = Simulation(self.levelName,
self.viewW,
self.viewH,
Color("white"))
#sim.window.AppPaintable = True
#sim.window.canvas.AppPaintable = True
#p = Picture(IMAGE_PATH+"woodFloor3.png")
#Set application background
#self.background = LRC_Background(IMAGE_PATH+"woodFloor3.png")
self.background = LRC_Background(IMAGE_PATH+"full_background.png")
pixmap = Gdk.Pixmap(None, 3000, 1500, 24)
#pixmap = Gdk.Pixmap(None, 397, 800, 24)
#pixmap = Gdk.Pixmap(None, 10, 10, 24)
with Gdk.CairoHelper.Create(pixmap) as g:
# The -1 here is to fix a bug where the left most column of pixels
# would not be painted
g.SetSourceSurface(self.background.background, -1, 0)
g.Paint()
sim.window.canvas.BinWindow.SetBackPixmap(pixmap, False)
#sim.window.setBackground(Color("black"))
#sim.window.canvas.BinWindow.AppPaintable = True
#create the master frame
self.masterFrame = MasterFrame(0,0)
self.masterFrame.levelWindow = self
self.masterFrame.draw(sim.window)
#Set world background
rec = Rectangle((self.vX,self.vY),
(self.vX+self.viewW,self.vY+self.viewH),
color=Color("grey"))
rec.draw(self.masterFrame)
self.worldFrame = WorldFrame(0,0)
self.worldFrame.levelWindow = self
self.worldFrame.draw(self.masterFrame)
self.uiFrame = Frame(0,0)
self.uiFrame.draw(self.masterFrame)
#sim.window.addScrollbars(wW+100,wH+100)
setOptionSim("fast",True)
setOptionSim("random",False)
#Turned the walls into rectangles for debugging purpose
#creates the border around the panel1
#top border
rec = Rectangle((self.vX,self.vY),
(self.vX+self.panel1W,self.vY+bW),
color=Color("black"))
rec.tag="outer wall 1"
rec.bodyType = "static"
rec.draw(self.worldFrame)
rec.addToPhysics()
#sim.window.draw(rec)
#left border
rec = Rectangle((self.vX,self.vY),
(self.vX+bW,self.vY+self.panel1H),
color=Color("black"))
rec.tag="outer wall 2"
rec.bodyType = "static"
rec.draw(self.worldFrame)
rec.addToPhysics()
#sim.window.draw(rec)
#right border
rec = Rectangle((self.vX+self.panel1W-bW,self.vY),
(self.vX+self.panel1W,self.vY+self.panel1H),
color=Color("black"))
rec.tag="outer wall 3"
rec.bodyType = "static"
rec.draw(self.worldFrame)
rec.addToPhysics()
#sim.window.draw(rec)
#bottom border
rec = Rectangle((self.vX,self.vY+self.panel1H),
(self.vX+self.panel1W,self.vY+self.panel1H+bW),
color=Color("black"))
rec.tag="outer wall 4"
rec.bodyType = "static"
rec.draw(self.worldFrame)
rec.addToPhysics()
#sim.window.draw(rec)
sim.userThread=self.gameThread#self.updateClock
sim.window.onMouseDown(self.mouseDown)
sim.window.onKeyPress(self.keyPressed)
return sim
def _setupPanel1(self,panel1):
"""
create a dummy rectangle that will be replaced with images, briefing
prompts, or other prompts
Args:
panel1: A retangle used as the canvas for panel 1.
"""
dummyShape=Picture(IMAGE_PATH+"blankPrompt.png",outline=makeColor(0,0,0,0))
##Rectangle((0,0),(10,10),color=Color("blue"))
dummyShape.visible=True
dummyShape.draw(panel1)
def _setupPanel2(self,panel2):
"""
Paints the bottom panel on the provided rectangle.
Args:
panel2: A rectangle used a the canvas for panel 2.
"""
#shortcut variables for panel width and height
p2W=panel2.width
p2H=panel2.height
fS=12 #font size
pad=1 #padding around border for panel2 objects
rad=5 #radius of click buttons
spacer=5
#offset of panel2 center to top left origin
oX=p2W/2-pad
oY=p2H/2
#levelName=
createPanelText(panel2,2*pad,pad,self.levelName,Color("black"),fS)
if self.maxTime==60:
timeText="01:00:00"
else:
timeText="00:00:00"
createPanelText(panel2,2*pad,pad+p2H/2,timeText,Color("black"),fS)
objPrompt=createPanelText(panel2,pad+(p2W/3)*0.5,pad,"Objective:",Color("black"),fS)
#self.objectiveText=createPanelText(panel2,pad+(p2W/5)*0.60,pad+p2H/4,"",Color("black"),fS)
self.objectiveText=createPanelText(panel2,pad+(p2W/3)*0.60,pad,"",Color("black"),fS,yJust="top")
objTri=Polygon((objPrompt.x-10,objPrompt.y),(objPrompt.x,objPrompt.y-5),
(objPrompt.x-10,objPrompt.y-10))
objTri.fill=Color("green")
objTri.draw(panel2)
createPanelText(panel2,pad+3*(p2W/5),pad,"Score:",Color("black"),fS)
createPanelText(panel2,pad+3*(p2W/5),pad+p2H/2,str(self.levelScore),Color("black"),fS)
buttonH=(p2H/2)-pad
if self.parent==None:
createPanelButton(panel2,2*p2W/3,pad,p2W/9,buttonH,"Prev","Prev",None,Color("white"),Color("gray"),fS)
createPanelButton(panel2,2*p2W/3+p2W/9,pad,p2W/9,buttonH,"Next","Next",None,Color("white"),Color("gray"),fS)
createPanelButton(panel2,2*p2W/3+2*p2W/9,pad,p2W/9,buttonH,"Menu","Menu",None,Color("white"),Color("gray"),fS)
else:
createPanelButton(panel2,2*p2W/3+2*p2W/9,pad,p2W/9,buttonH,"Menu","Menu",None,Color("white"),Color("black"),fS)
if self.parent.prevLevelExists():
createPanelButton(panel2,2*p2W/3,pad,p2W/9,buttonH,"Prev","Prev",None,Color("white"),Color("black"),fS)
else:
createPanelButton(panel2,2*p2W/3,pad,p2W/9,buttonH,"Prev","Prev",None,Color("white"),Color("gray"),fS)
if self.parent.nextLevelExists()=="level exists":
createPanelButton(panel2,2*p2W/3+p2W/9,pad,p2W/9,buttonH,"Next","Next",None,Color("white"),Color("black"),fS)
else:
createPanelButton(panel2,2*p2W/3+p2W/9,pad,p2W/9,buttonH,"Next","Next",None,Color("white"),Color("gray"),fS)
createPanelButton(panel2,2*p2W/3,p2H/2,p2W/9,buttonH,"Quit","Quit",None,Color("white"),Color("black"),fS)
createPanelButton(panel2,2*p2W/3+p2W/9,p2H/2,p2W/9,buttonH,"Restart","Restart",None,Color("white"),Color("black"),fS)
createPanelButton(panel2,2*p2W/3+2*p2W/9,p2H/2,p2W/9,buttonH,"Dismiss","Dismiss",None,Color("white"),Color("black"),fS)
def _addHelpWidget(self, panel3, title, buttonList, height):
self.helpWidgets.append(HelpPageWidget(panel3,(0,self.helpPagesY),(panel3.width,self.helpPagesY+height),title,buttonList,"helpWidget:" +str(len(self.helpWidgets)),debug=False))
self.helpPagesY+=height
def _setupPanel3(self,panel3):
"""
Draws all side panel elements to the provided rectangle.
Args:
panel3: A Rectangle on which to draw the elements.
"""
pad=5 #padding between stuff
buttonW = 55
buttonH = 20
rad=5 #radius of round rectangles
fS=12 #font size
# Create title
helpPagesTitle = createPanelText(panel3,5,0,"Help Pages:",c=Color("black"),fS=18)
L2=createPanelLine(panel3,(pad,0+helpPagesTitle.fontSize+pad),(panel3.width-pad,0+helpPagesTitle.fontSize+pad),lw=3)
#helpWidgets is buggy. You have to make sure the helpWidget box is big enough to be able to clcik the buttons
#Set debug to True to see the click box of the widget.
self.helpPagesY = 25
self._addHelpWidget(panel3, "Intro", self.IntroHelpPages, 125)
self._addHelpWidget(panel3, "Gauntlet", self.GauntletHelpPages, 100)
self._addHelpWidget(panel3, "Labyrinth", self.LabyrinthHelpPages, 75)
self._addHelpWidget(panel3, "Chamber", self.ChamberHelpPages, 100)
self._addHelpWidget(panel3, "Coming soon (tm)", self.FixmeHelpPages, 75)
self._addHelpWidget(panel3, "Closed Loop Control", self.ClosedLoopHelpPages, 100)
## self.helpWidgets.append(HelpPageWidget(panel3,(0,25),(panel3.width,150),"Intro",self.IntroHelpPages,"helpWidget:0",debug=False))
## self.helpWidgets.append(HelpPageWidget(panel3,(0,150),(panel3.width,250),"Gauntlet",self.GauntletHelpPages,"helpWidget:1",debug=False))
## self.helpWidgets.append(HelpPageWidget(panel3,(0,250),(panel3.width,350),"Labyrinth",self.LabyrinthHelpPages,"helpWidget:2",debug=False))
## self.helpWidgets.append(HelpPageWidget(panel3,(0,350),(panel3.width,450),"Chamber",self.ChamberHelpPages,"helpWidget:3",debug=False))
## self.helpWidgets.append(HelpPageWidget(panel3,(0,450),(panel3.width,500),"Closed Loop Control",self.ClosedLoopHelpPages,"helpWidget:4",debug=False))
# FIXME JH: Dirty hack to make sure the buttons for closed loop control don't show up in all levels.
# Need to design a system where we don't have to do this.
self.helpWidgets[4].setButtonsInvisible()
self.helpWidgets[5].setButtonsInvisible()
briefStartY=500
briefTitle=createPanelText(panel3,pad,600,"Briefings:",Color("black"),18)
L2=createPanelLine(panel3,(pad,600+briefTitle.height+pad),(panel3.width-pad,600+briefTitle.height+pad),lw=3)
#Add placeholder buttons that launch briefing screens.
for i in range(5):
self.briefButton.append(createPanelButton(panel3,pad,pad*i+buttonH*i+630,panel3.width-2*pad,buttonH,"Briefing "+str(i+1),"brief:"+str(i),fS=12))
self.briefButton[-1][0].visible=False
'''
#shortcut variables for panel width and height
p3W=panel3.width
p3H=panel3.height
#rowTitles=["Robot Actions:","Robot Sensors:","Programming:"]
rowText=[]
#tags go with the title
#rowTags=[["Move1","Move2","Pen","Beep"],["Line","Light","IR","Obstacle"],
# ["Shell","Scripts","Functions","Variables","if","while","for"]]
rowButtons=[]
#rowColors=[Color("red"),Color("lightblue"),Color("purple")]
rowLimit = 4 #number of buttons per row
pad=5 #padding between stuff
#offset of panel3 center to top left origin
oX=p3W/2-pad
oY=p3H/2-pad
buttonW = 55
buttonH = 20
rad=5 #radius of round rectangles
fS=12 #font size
currY=-oY #current y location for placing objects
#main title for this panel
lastText=createPanelText(panel3,pad,pad,"Help Pages:",Color("black"),18)
L1=Line((-oX,currY+19),(-oX+p3W,currY+19),color=Color("black"))
L1.draw(panel3)
L1.setWidth(3)
#print(lastText.getY()+p3H/2)
currY=lastText.getY()+p3H/2+buttonH+pad
#currY=buttonH+pad
for i in range(len(self.rowTitles)):
lastText=createPanelText(panel3,
pad,
currY,
self.rowTitles[i],
Color("black"),
15)
currY=lastText.getY()+p3H/2+buttonH#currY+buttonH
for j in range(len(self.rowTags[i])):
if j!=0 and j%rowLimit==0:
currY=currY+buttonH
[lastShape,lastText]=createPanelButton(panel3,
j%rowLimit*buttonW,
currY,
buttonW,
buttonH,
self.rowTags[i][j],
self.rowTags[i][j],
None,
self.rowColors[i],
Color("black"),
fS)
self.pages[self.rowTags[i][j]]=self.pageNames[i][j]
currY=lastShape.getY()+p3H/2+buttonH
'''
#############################
# UI Manipulation Functions #
#############################
#Direct all set, show,and hide of panel1 through these functions
#Could potentially enable more control of when panel1 appears and disappears
#Placing an image onto panel1
[docs] def setImagePanel1(self,imageFileName,show=True):
"""
Directly set an image as the contents of panel 1.
Args:
imageFileName: The name of an image file (png, jpg)
show: If True, the image will be displayed immediately
"""
if isinstance(imageFileName,str):
if os.path.isfile(imageFileName):
self.setShapePanel1(Picture(imageFileName),show=show)
else:
self.outputPrint("Error, "+imageFileName+" does not exist.",0)
else:
self.outputPrint("Error please enter a string filename",0)
[docs] def setShapePanel1(self,s,show=True,offX=None,offY=None):
"""
Directly set a shape as the contents of panel 1.
Args:
imageFileName: The name of an image file (png, jpg)
show: If True, the shape will be displayed immediately
"""
for i in range(len(self.uiFrame.shapes)):
if self.uiFrame.shapes[i].tag=="panel1":
if show:
self.showPanel1(self.uiFrame.shapes[i])
self.uiFrame.shapes[i].shapes[0]=s
w=self.uiFrame.shapes[i].shapes[0].width
h=self.uiFrame.shapes[i].shapes[0].height
if offX==None and offY==None:
self.uiFrame.shapes[i].shapes[0].moveTo(0,0)
else:
self.uiFrame.shapes[i].shapes[0].move(offX,offY)
#resets the points in self.panel1.shapes[0]
self.panel1=self.uiFrame.shapes[i]
#set the drawn_on_shape variable so clicks register properly
self.panel1.shapes[0].drawn_on_shape=self.panel1
return
[docs] def showPanel1(self,p=None):
"""
Show the shape that is currently set to panel 1.
Args:
p: Optional pointer to the shape currently displayed in panel 1.
Providing this pointer slighlty increases performance.
"""
#if whatever function calling this show has a handle to panel1
#there's no need to refind it
if p!=None:
p.visible=True
else:
for i in range(len(self.uiFrame.shapes)):
if self.uiFrame.shapes[i].tag=="panel1":
self.uiFrame.shapes[i].visible=True
return
#self.sim.window.canvas.shapes[i]=Picture(message)
#self.sim.window.canvas.shapes[i].moveTo(self.p1X+self.panel1W/
#2,self.p1Y+self.panel1H/2)
#self.sim.window.canvas.shapes[i].visible=True
#return
#Hides whatever object is drawn to panel1
[docs] def hidePanel1(self,p=None):
"""
Hide the shape that is currently set to panel 1.
Args:
p: Optional pointer to the shape currently displayed in panel 1.
Providing this pointer slighlty increases performance.
"""
if p!=None:
p.visible=False
else:
for i in range(len(self.uiFrame.shapes)):
if self.uiFrame.shapes[i].tag=="panel1":
self.uiFrame.shapes[i].visible=False
return
[docs] def isVisiblePanel1(self):
"""
Checks whether panel 1 is visible.
Return:
True if panel 1 is visible, and False otherwise.
"""
for i in range(len(self.uiFrame.shapes)):
if self.uiFrame.shapes[i].tag=="panel1":
return self.uiFrame.shapes[i].visible
[docs] def dismiss(self):
"""
Function that performs same action as the Dismiss button or pressing 'b'.
Dismisses/hides whatever is on Panel1
"""
if self.isVisiblePanel1():
self.pageTag=None
self.hidePanel1()
[docs] def updateClock(self):
"""Callback that updates the clock"""
if self.runTimer:
elapse=time()-self.startTime
timeScore=self.maxTime-elapse
if timeScore<=0:
timeScore=0
m=int(timeScore/60)
sec=int(timeScore%60)
ss=int((timeScore%60-sec)*100)
txt=str(m).zfill(2)+":"+str(sec).zfill(2)+":"+str(ss).zfill(2)
self.panel2.shapes[1].setText(txt)
[docs] def flashObjective(self):
if self.objectiveText.visible:
self.objectiveText.visible=False
else:
self.objectiveText.visible=True
[docs] def setObjectiveText(self,message,numFlashes=0):
"""
Sets the current objective text.
Args:
message: A string containing the message to be displayed.
"""
self.objectiveText.setText(message)
if numFlashes>0:
#2 times numFlahes b/c the text will go invisible and then visible
objectiveFlashEvent = RepeatedActionEvent(0.4,numFlashes*2,self.flashObjective)
self.addStepEvent(objectiveFlashEvent)
[docs] def clearObjectiveText(self):
"""Sets the objective text to the empty string."""
self.objectiveText.setText("")
[docs] def addEntryObject(self,e):
"""Adds an entry object for the window to keep track of.Entry object focused considered during mouse down and key press"""
self.entryObjects.append(e)
[docs] def clearEntryObject(self,e):
"""Clears list of entry objects. Entry object focused considered during mouse down and key press"""
self.entryObjects=[]
[docs] def addBriefShape(self,s,text=None):
"""
Adds a shape to the list of briefing shapes.
Args:
s: A reference to the shape to be added.
text: Sets the text of the button associated with this briefing.
"""
if isinstance(s,Shape):
self.briefShapeList.append(s)
i=len(self.briefShapeList)-1
if i<len(self.briefButton):
self.briefButton[i][0].visible=True
if text:
self.briefButton[i][1].setText(text)
[docs] def launchBriefScreen(self,index):
"""
Will display the indicated briefscreen on top of the game area.
Args:
index: Integer indicating the index of the briefscreen to be shown.
"""
if index<self.briefShapeList:
if isinstance(self.briefShapeList[index],Shape):
self.setShapePanel1(self.briefShapeList[index],show=True)
[docs] def clearBriefList(self):
"""
Clears the list of briefscreens.
Should probably not be called from within a level.
"""
self.briefShapeList=[]
#helper pages for retrieving the displayName, tag, and color of a help page button
#info is passed to some type of briefing function or help page in order to add clickable buttons
[docs] def getI(self,buttonName):
for i in range(len(self.helpWidgets)):
#Find the widget with this button name
if buttonName in self.helpWidgets[i].buttonAndText:
return self.getHelpPageButtonInfo(i,buttonName,"Inline")
[docs] def getR(self,buttonName):
for i in range(len(self.helpWidgets)):
#Find the widget with this button name
if buttonName in self.helpWidgets[i].buttonAndText:
return self.getHelpPageButtonInfo(i,buttonName,"Related")
[docs] def getHelpPageButtonInfo(self,helpWidgetIndex,buttonName,returnType=None):
buttonAndText=self.helpWidgets[helpWidgetIndex].buttonAndText[buttonName]
button=buttonAndText[0]
if returnType=="Inline":
return Inline2(buttonName,"helpPage:"+button.tag, button.fill)
elif returnType=="Related":
return [Related(buttonName,"helpPage:"+button.tag, button.fill), " "]
else:
[buttonName,"helpPage:"+button.tag, button.fill]
[docs] def checkFocus(self):
"""
Helper function that iterates through the list of entry objects that could be added
to this window. If any of the foucs for the entries are true than the focus for the
main window is set to False. Mainly used to prevent the mouse and key presses from
interrupting the text entries
"""
self.winFocus=True
for e in self.entryObjects:
if e.focus:
self.winFocus=False
continue
[docs] def addMouseDown(self,f):
"""
It's assumed the developer knows what they are doing and are adding a proper function 'f'
with that has the fields (obj,event)
"""
self.sim.window.onMouseDown(f)
######################
# Callback functions #
######################
[docs] def mouseDown(self,obj, event):
"""
Callback function for when the window is clicked.
Overwrite this function to execute a specific action when the user
clicks the screen.
Args:
obj: A pointer to the screen being clicked on, which should be the
level window itself.
event: The event triggering this callback. Can be read to discover
which button was clicked, and where the mouse pointer was.
"""
self.checkFocus()
if self.debug and self.winFocus:
print(event.x,event.y)
[docs] def keyPressed(self,obj,event):
"""
Callback function for when a key is pressed.
This function can be overwritten to implement custom keystrokes, but do
not forget to call this function at the end (LevelWindow.keyPressed),
otherwise basic keystrokes like closing the window or restarting the
level will no longer work.
Args:
obj: A pointer to the level window object.
event: The event triggering this callback. Can be read to discover
which key was pressed.
"""
self.checkFocus()
if self.winFocus:
if str(event.key)=="q" or str(event.key)=="Q":
self.quitGame()
elif str(event.key)=="r" or str(event.key)=="R":
self.restartGame()
elif (str(event.key)=="n" or str(event.key)=="N"):
self.gotoNextLevel()
elif (str(event.key)=="p" or str(event.key)=="P"):
self.gotoPrevLevel()
#dismisses panel1 screen as well as the speechbox
elif (str(event.key)=="d" or str(event.key)=="D"):
if self.isVisiblePanel1():
self.pageTag=None
self.hidePanel1()
elif str(event.key)=="F1":
self.debugConsole()
#else:
# self.createBriefScreen()
# self.showPanel1()
#Hides the speech box if visible.
#Might have to add the condition where you check if
#the conversation is done before you allow the
#speechbox to be hidden, e.g.
#if self.conversation.currentResponse().text=="":
#RV
#if self.speechBox.frame.visible:
# self.speechBox.hide()
[docs] def panel1Click(self,obj,event):
"""
Callback for when panel 1 is clicked.
Can be overwritten to launch a cusomt event when the game area is
clicked.
Args:
obj: A pointer to the level window object.
event: The event triggering this callback. Can be read to discover
which button was clicked, and where the mouse pointer was.
"""
if self.debug:
print("Clicking panel 1 at ",event.x,event.y)
p=self.panel1.center
cX=p[0]
cY=p[1]
#Clicked the object in self.panel1.shapes[0]
if self.panel1.shapes[0].hit(event.x,event.y) and self.isVisiblePanel1():
#print("hit the display object")
for s in self.panel1.shapes[0].shapes:
if s.hit(event.x,event.y) and s.tag: #not None
#print("Hit object ",s.tag)
if self.pageTag==s.tag:
#hide the page
self.hidePanel1()
self.pageTag=None
else:
if(s.tag.find("helpPage:")!=-1):
self.setImagePanel1(self.pages[s.tag.split(":")[-1]])
self.pageTag=s.tag.split(":")[-1]
elif s.tag.find("brief:")!=-1:
index=int(s.tag.split(":")[-1])
#print("index is ",index)
self.launchBriefScreen(index)
self.pageTag=s.tag
[docs] def panel2Click(self,obj,event):
"""
Callback for when panel 2 is clicked.
Should probably not be overwritten, as it handles basic button presses.
Args:
obj: A pointer to the level window object.
event: The event triggering this callback. Can be read to discover
which button was clicked, and where the mouse pointer was.
"""
p=self.panel2.center
cX=p[0]
cY=p[1]
for s in self.panel2.shapes:
#JH: This change only works in the new version of Calico
#Since Calico calls hit internally as well, I don't know
#how to make this backward compatible
if s.hit(event.x,event.y):
if s.tag=="Restart":
self.restartGame()
elif s.tag=="Quit":
self.quitGame()
elif s.tag=="Prev":
self.gotoPrevLevel()
elif s.tag=="Next":
self.gotoNextLevel()
elif s.tag=="Menu":
if self.parent==None:
pass
else:
self.quitGame()
elif s.tag=="Dismiss":
if self.isVisiblePanel1():
self.hidePanel1()
#else:
#This is a hack for now. Eventually when the brief screens are created
#save the shape so I don't have to regenerate it. The shape will
#be shave to self.briefScreen, but first all other instances of
#self.briefScren need to be cleared in the refactor. RV
# self.createBriefScreen()
# self.showPanel1()
#if self.speechBox.frame.visible:
# self.speechBox.hide()
[docs] def helpPageEvents(self,clickedPanel,event):
"""
Defunked function. Kept around for examples of code. RV.
Callback for when panel 3 is clicked.
Should probably not be overwritten, as it handles basic button presses.
Args:
clickedPanel: A pointer to the panel that was clicked.
event: The event triggering this callback. Can be read to discover
which button was clicked, and where the mouse pointer was.
"""
#required to register the correct click location
p=clickedPanel.center
cX=p[0]
cY=p[1]
#for s in self.panel3.shapes:
for p in clickedPanel.shapes:
#JH: This change only works in the new version of Calico
#Since Calico calls hit internally as well, I don't know
#how to make this backward compatible
if p.hit(event.x,event.y):
if s.tag.find("brief")!=-1:
#if you click the same page again
if self.pageTag==s.tag:
#hide the page
self.hidePanel1()
self.pageTag=None
#if the page you click isn't the same as the previous tag
else:
index=int(s.tag.split(":")[-1])
#print("index is ",index)
self.launchBriefScreen(index)
self.pageTag=s.tag
if s.tag in self.pages:
#close brief screen if up
#if self.briefScreen.visible:
# self.briefScreen.visible=False
#if you click the same page again
if self.pageTag==s.tag:
#hide the page
self.hidePanel1()
self.pageTag=None
#if the page you click isn't the same as the previous tag
else:
self.setImagePanel1(self.pages[s.tag])
self.pageTag=s.tag
return
[docs] def briefScreenClick(self,obj,event):
"""
Callback for when a briefscreen is clicked.
Should probably not be overwritten, as it handles basic button presses.
Args:
obj: A pointer to the level window object.
event: The event triggering this callback. Can be read to discover
which button was clicked, and where the mouse pointer was.
"""
self.helpPageEvents(self.briefScreen,event)
[docs] def panel3Click(self,obj,event):
"""
Callback for when panel 3 is clicked.
Should probably not be overwritten, as it handles basic button presses.
Args:
obj: A pointer to the level window object.
event: The event triggering this callback. Can be read to discover
which button was clicked, and where the mouse pointer was.
"""
#required to register the correct click location
p=self.panel3.center
cX=p[0]
cY=p[1]
#temporary variable so I don't have to repeat the code for hiding the panel twice
clickedTag=None
for s in self.panel3.shapes:
#TODO: In theory you should also check if the clicked item is visible.
#This goes more for the help pages than the briefing screens. I'm not
#adding the condition b/c it could help with debugging and might be a fun
#bug/easter egg/secret. RV
#if s.hit(event.x-cX,event.y-cY):
# JH: The offsets should no longer be required
# (and if fact, should break the buttons)
if s.hit(event.x,event.y):
#print("clicked tag is ",s.tag)
if s.tag.find("brief:")!=-1:
#print("clicked on brief screen")
clickedTag=s.tag
elif s.tag.find("helpWidget:")!=-1:
#print("click on help page")
index=int(s.tag.split(":")[-1])
#print("Searching for page hit at ",e.x-cX,e.y-cY)
# JH: The offsets should no longer be required
#clickedTag=self.helpWidgets[index].getClickedButton(event.x-cX,event.y-cY)
clickedTag=self.helpWidgets[index].getClickedButton(event.x,event.y)
else:
#print("Not recognized tag ",s.tag)
clickTag=s.tag
break
if clickedTag:
#print("Going into clicked Tagged section. Original tag is ",s.tag)
#if the page you click is the same as the previous tag hide it
if self.pageTag==clickedTag:
#hide the page
self.hidePanel1()
self.pageTag=None
else:
if s.tag.find("brief:")!=-1:
index=int(s.tag.split(":")[-1])
#print("index is ",index)
self.launchBriefScreen(index)
self.pageTag=s.tag
elif s.tag.find("helpWidget:")!=-1:
self.setImagePanel1(self.pages[clickedTag])
self.pageTag=clickedTag
else:
#TODO: If the user doesn't click on a help button on the help widget you'll come here
#That's fine. Just hide panel1 and no need to print the message below
print("Clicked on object with unknown tag")
print(clickedTag)
#self.helpPageEvents(self.panel3,e)
###############################
# Functions to Control Calico #
###############################
[docs] def getScopeVariable(self,name=None):
"""
Function to retrieve variables in scope
"""
if self.exists(name):
return calico.manager.scope.GetVariable(name)
else:
return None
[docs] def exists(self,name=None):
"""
Function to check if a variable is in scope
"""
try:
calico.manager.scope.GetVariable(name)
except:
return False
else:
return True
[docs] def burnEvidence(self):
"""
Method for clearing the output and history.
Useful when someone enters a password and you
want to clear you're tracks.
"""
if self.gameOver==0:
#calico.clear_output()
calico.Output.Buffer.Text=""
calico.history.clear()
self.outputPrint("The Calico Project, Version 3.1.1\n",2)
[docs] def outputPrint(self,message,tagNo=0):
"""
Method for printing colored text to the output.
Args:
message: String containing the message to be displayed.
tagNo: Integer indicating in which of the predetermined colors the
text will be printed.
"""
calico.PrintLine(self.printTags[tagNo].Key, message)
[docs] def stopScript(self):
"""Stops the current script from running, if any."""
if calico.isRunning and calico.CurrentDocument is not None:
calico.CurrentDocument.Stop()
else:
calico.AbortThread()
[docs] def setErrorCallback(self,callback):
"""
Adds a callback that is called whenever an error is thrown in Calico
The callback must have an argument for the exception thrown and the initial command
Exp: def expCallback(e,command):
"""
calico.manager["python"].engine.OnErrorCallback=callback
############################
# Game Mechanics Functions #
############################
[docs] def saveStats(self):
"""Saves the currenlty recorded statistics to a file (I guess?)"""
name=self.levelName
score=self.levelScore
if self.startTime!=None:
elapseTime=round(time()-self.startTime,2)
else:
elapseTime=0
#TODO: make better flags in order to count things like quits
#and transitions to next or prev level
if self.gameOver==0: #restart, quitgame, next, or prev level
win=0
winTime=0
lose=0
restart=1
elif self.gameOver==-1: #lose
win=0
winTime=0
lose=1
restart=0
elif self.gameOver==1: #win
win=1
winTime=elapseTime
lose=0
restart=0
else: #should never be reached
win=0
winTime=0
lose=0
restart=0
#save data
if self.parent != None:
self.parent.saveGameData([score,elapseTime,win,winTime,lose,restart])
#stat=statObject(name,score,elapseTime,win,winTime,lose,restart)
#self.gameData.updateStats(stat)
#self.gameData.updateStats(self.levelName,[score,elapseTime,win,winTime,lose,restart])
#self.updateGameData([score,elapseTime,win,winTime,lose,restart])
## def updateGameData(self,data):
## if self.levelName not in self.gameData["ScribblerAdventure"]["levelData"]:
## self.gameData["ScribblerAdventure"]["levelData"][self.levelName]={}
## self.gameData["ScribblerAdventure"]["levelData"][levelName]["logins"]=[]
## self.gameData["ScribblerAdventure"]["levelData"][levelName]["stats"]=[]
##
## self.gameData["ScribblerAdventure"]["levelData"][self.levelName].append(data)
##
[docs] def levelTransition(self):
"""
All the steps required for a level transition (quit, menu, next,
prev, and restart) that have to be done to save data and to
prevent crashing
"""
#very import or calico will crash trying to reinit the level
self.stopScript()
#accumalte stats
self.saveStats()
[docs] def restartGameNoStopScript(self):
"""
Version of restartGame that doesn't stop scripts
used for the restart() user command
may cause calico to crash if used improperly. RV.
"""
#stop any script and saves
#self.levelTransition()
tempScore=self.levelScore
self.__init__(self.gameData,self.parent)
#hides the brief screen
self.hidePanel1()
[docs] def restartGame(self):
"""Stops all scripts and restarts the level."""
#stop any script and saves
self.levelTransition()
tempScore=self.levelScore
#tempStatusOn=self.statusOn
self.__init__(self.gameData,self.parent)
#hides the brief screen
self.hidePanel1()
[docs] def quitGame(self):
"""Saves statistics and exits the game"""
self.levelTransition()
self.sim.window.Destroy()
if self.parent != None:
self.parent.showWindow()
## if self.parent != None:
## self.parent.onReturn()
[docs] def setGameOver(self,value):
"""
Indicates that the game is over, for better or worse.
Args:
value: 1 indicates that the player has won the game, -1 indicates
that the player has lost the game.
"""
try:
if self.gameOver == 0:# and self.runTimer:
self.gameOver=value
r=getRobot()
if r:
r.stop()
self.runTimer=False
endTime=time()
if self.gameOver==-1:
self.setImagePanel1(IMAGE_PATH+"loseGamePrompt.png")
else:
timeBonus=0
self.levelScore+=100+timeBonus
if self.parent==None:
#print("You won the game!")
self.setImagePanel1(IMAGE_PATH+"winGamePromptNoEvent.png")
else:
unlocked=self.parent.levelSolved()
self.setImagePanel1(IMAGE_PATH+"winGamePromptEvent.png" )
self.launchEndEvents()
except:
print(traceback.format_exc())
[docs] def loseGame(self):
"""
The player loses the game.
Short for setGameOver(-1). This can be easier to use than setGameOver(-1)
because it does not require a lambda when used in a callback function.
"""
self.setGameOver(-1)
[docs] def winGame(self):
"""
The player wins the game.
Short for setGameOver(1). This can be easier to use than setGameOver(1)
because it does not require a lambda when used in a callback function.
"""
self.setGameOver(1)
[docs] def confirmDebugCommand(self,success,message):
if success:
print("Success at "+message)
else:
print("Failure at "+message)
[docs] def debugConsole(self):
"""
Launches and an input dialog box where you can enter commands that can be used
to debug and quickly move around the levels
"""
cmd=ask("Enter debug command.")
print(cmd)
if self.parent!=None:
if cmd=="incrementHighestStage":
self.parent.setHighestStage(self.gameData["ScribblerAdventure"]["highestStage"]+1)
self.confirmDebugCommand(True,"Incrementing highest stage.")
elif cmd=="decrementHighestStage":
self.parent.setHighestStage(self.gameData["ScribblerAdventure"]["highestStage"]-1)
self.confirmDebugCommand(True,"Decrementing highest stage.")
elif cmd=="incrementHighestLevel":
self.parent.setHighestLevel(self.gameData["ScribblerAdventure"]["highestLevel"]+1)
self.confirmDebugCommand(True,"Incrementing highest level.")
elif cmd=="decrementHighestLevel":
self.parent.setHighestLevel(self.gameData["ScribblerAdventure"]["highestLevel"]-1)
self.confirmDebugCommand(True,"Decrementing highest level.")
elif cmd.startswith("setHighestStage"):
try:
highestStage = int(cmd.split()[1])
self.parent.setHighestStage(highestStage)
self.confirmDebugCommand(True,"Set highest stage to: " + str(highestStage))
except:
self.confirmDebugCommand(False,"Setting highest stage")
elif cmd.startswith("setHighestLevel"):
try:
highestLevel = int(cmd.split()[1])
self.parent.setHighestLevel(highestLevel)
self.confirmDebugCommand(True,"Set highest level to: " + str(highestLevel))
except:
self.confirmDebugCommand(False,"Setting highest stage")
if cmd=="loseCurrentLevel":
self.loseGame()
self.confirmDebugCommand(True,"Losing current level.")
elif cmd=="winCurrentLevel":
self.winGame()
print("got here")
self.confirmDebugCommand(True,"Winning current level")
[docs] def checkPassword(self,testPass):
"""
Checks if the incoming string matches the password.
Ends the game if password is correct. Launches
an incorrect password message if incorrect."
"""
fail=True
if isinstance(testPass,str):
entry=testPass.ToLower()
if entry=="lrcmentor":
#self.burnEvidence()
self.speechBox.hide()
self.setGameOver(1)
fail=False
if fail:
#self.setResponse("Incorrect Password.")
self.addResponse(IMAGE_PATH+"lockClosed.png","Incorrect Password. This incident will be reported.")
talk()
[docs] def lockEndPad(self):
"""
Helper function that unlocks the end pad through
the self.endPadLocked variable. This variable can
be used to lock the end pad until the user accomplishes
some task in the level.
"""
self.endPadLocked=True
[docs] def unlockEndPad(self):
"""
Helper function that locks the end pad through
the self.endPadLocked variable. This variable can
be used to lock the end pad until the user accomplishes
some task in the level.
"""
self.endPadLocked=False
[docs] def onEndPad(self):
if self.tempContact:
if self.tempContact.IsTouching():
return True
else:
return False
else:
return False
[docs] def gameThread(self):
"""
This is the main function passed to Myro
it calls the update clock and a another virtual function that
can be used in different levels
"""
self.updateClock()
try:
self.levelThread()
except:
print(traceback.format_exc())
[docs] def gotoNextLevel(self):
"""Transitions to the next level, if possible."""
self.levelTransition()
if self.parent!=None:
self.parent.launchNextLevel()
[docs] def gotoPrevLevel(self):
"""Transitions to the previous level, if possible."""
self.levelTransition()
if self.parent!=None:
self.parent.launchPrevLevel()
##########################
##### User Functions #####
##########################
#User commands are defined here, but are loaded into memory by importing
#the userFunctions.py file
[docs] def pickup(self):
"""
Function for picking up items from the game world.
Return:
The item that was picked up, or a string containing an error message
if the pickup failed.
"""
wait(0.0000001)
self.pickupContactList = cleanContactList(self.pickupContactList)
for item, _ in self.pickupContactList:
if item.visible():
item.hide()
item.onPickup()
return item.returned
self.printToSpeechBox(PICKUP_FAILED)
return PICKUP_FAILED
[docs] def skip(self):
"""
Function to skip dialogue. Also dismisses whatever is on panel 1.
"""
#TODO: skip() doesn't work if you put it and restart() at the top of a Script
#restart()
#skip()
#forward(4,1)
if self.debug: print("DEBUG: In skip function.")
while len(self.queue.events) > 0 or self.conversation.blocked:
if self.debug: print("DEBUG: Spinning in skip:", len(self.queue.events), self.conversation.blocked)
pass
self.conversation.getActiveThread().skip()
if self.conversation.getActiveThread().blockNextSkip:
# One of our actions has invoked the block skip,
# possibly to notify the user of an error that should not be hidden,
# do not hide the speech box.
#
# Leave the conversation in the state that the action calling blockSkip left it.
self.conversation.getActiveThread().blockNextSkip = False
if self.debug: print("DEBUG: Leaving skip function through blockSkip.")
return
self.speechBox.hide()
self.addStepEvent(self.speechBox.hide)
#Dismiss commands for panel1
if self.isVisiblePanel1():
self.pageTag=None
self.hidePanel1()
# Wait for animations to finish
while len(self.queue.events) > 0:
if self.debug:
print("DEBUG: Waiting for animation to finish.")
print("DEBUG: Animation parts left:", len(self.queue.events))
# Finish the conversation
if self.conversation.autoUnpause:
self.pauseRobot(False)
self.conversation.autoUnpause = False
self.closeDialogOnMove = True
if self.debug: print("DEBUG: Leaving skip function.")
#wait(0.5)
[docs] def talk(self):
"""
Function for talking.
Returns:
Whatever is set to be returned, which is usually a string containing
the spoken text.
"""
if self.debug: print("DEBUG: In talk function.")
#print("Talk (1601):", "x" in userGlob)
while len(self.queue.events) > 0 or self.conversation.blocked:
if self.debug: print("Spinning in talk", len(self.queue.events), self.conversation.blocked)
pass
thread = self.conversation.getActiveThread()
hasNext = thread.hasNext()
response = thread.currentResponse()
text = response.text
portrait = response.portrait
if text != "":
self.printToSpeechBox(text, portrait, self.closeDialogOnMove,
hasNext=thread.hasNext())
elif self.speechBox.visible():
self.speechBox.hide()
else:
self.printToSpeechBox("There is no one to talk to.")
thread.nextResponse()
response.launchActions()
if self.conversation.getActiveThread().blockNextSkip:
# If block skip is set, it probably means that the conversation was
# extended. Recheck if the conversation has been extended.
self.conversation.getActiveThread().blockNextSkip = False
#hasNext = self.conversation.getActiveThread().hasNext()
if not self.conversation.getActiveThread().hasNext() and self.conversation.autoUnpause:
self.pauseRobot(False)
self.conversation.autoUnpause = False
self.closeDialogOnMove = True
# actionEvent = StaticEvent(action, remove=True)
# self.addStepEvent(actionEvent)
if self.debug: print("DEBUG: Leaving talk function.")
return response.returned
[docs] def use(self, items):
"""
Attempts to use the items passed by the user.
Args:
items: The items passed by the used, which can literaly be anything.
Return:
This function may return arbitrary objects based on whatever the use
function did or did not accomplish. If the use function failed, a
string containing an error message is returned.
"""
# Use one of the items if one of them is usable (slow?)
for item in items:
if hasattr(item, "canUse") and hasattr(item, "onUse"):
if item.canUse:
try:
# Allows multiple items to be used simultaniously
return item.onUse(items)
except TypeError:
# Call for backward compatibility
return item.onUse()
# Wait one simulation step, so the collision with the object has time to happen
wait(0.00001)
# The seperation doesn't always trigger (happens when the object is moved
# outside of the physics environment). For these cases, clean them here.
self.interactContactList = cleanContactList(self.interactContactList)
for contactTuple in self.interactContactList:
interact, _ = contactTuple
if interact.canInteract:
return interact.onInteract(items)
# Print our failure to interact
self.printToSpeechBox(USE_FAILED)
return USE_FAILED
#Note there is a restart() user command that essentially calls def
#restartGameNoStopScript(self)
###################################
# Virtual Functions To Overwrite #
###################################
#The sections after this provide helper functions that could be placed in
#these virtual funcitons.
[docs] def onStartLevel(self):
"""Called right before the player gains control."""
pass
[docs] def createLevel(self):
"""Virtual function that should overwritten to create a level."""
pass
##############################
# On Level StartUp Functions #
##############################
[docs] def getBriefObject(self):
"""Function returns a Shape with text and buttons drawn on it."""
pass
#RV: Now that there's getBriefObject, can this function be deleted?
[docs] def buildBriefing(self):
"""Function to be overwritten to build the briefing"""
pass
[docs] def createBriefScreen(self):
"""Creates the briefscreen."""
briefImage=self.getBriefImage()
if briefImage:
self.setShapePanel1(briefImage,show=False)
else:
self.setImagePanel1(IMAGE_PATH+"blankBriefWithText.png",show=False)
# Probably need a way to check if value is a bool. RV
# TODO: Add stop() to the updateSpeed in C# code. RV
[docs] def pauseRobot(self,value,index=None):
"""
Helper function to stop the robot.
Args:
value: True to pause the robot, False to unpause.
index: The index of the robot to be paused. If no index is provided
all robots are paused.
"""
if index==None:
for r in self.robots:
r.pauseRobot=value
if value==True:
r.stop()
else:
if index<len(self.robots):
self.robots[index].pauseRobot=value
if value==True:
self.robots[index].stop()
##############Conversation Functions################
[docs] def printToSpeechBox(self,
text,
portrait=None,
closeOnMove=True,
closeOnTime=0,
hasNext=False):
"""
Prints a message directly to the speech box.
Args:
text: The text to be printed (str)
portrait: The portrait to be show with the string. Can be a
Calico Picture object or a string representing the
location of an image file to be loaded. A default
Scribbler image is shown if None is passed.
closeOnMove: If true, an on move event will be scheduled that closes
the box as soon as the robot moves.
closeOnTime: If true, a timed event will be scheduled that closes
the box after a certain number of ticks. Can not be
used with 'closeOnMove'. If both options are
true, only the onMoveEvent will be scheduled.
hasNext: Boolean the will tell the speechbox if there is more
conversation left. Should be false if this is a one-of
message.
"""
try:
#Create a new event
showBoxEvent = ShowBoxEvent(self)
#Set portrait
if not portrait:
portrait=makePic(IMAGE_PATH + "scribblerPortrait.png")
elif not isinstance(portrait, Picture):
portrait=makePic(portrait)
showBoxEvent.portrait = portrait
if self.conversation.speechBoxYPos is None:
# Determine wheter the speech box will appear at the top or bottom
# of the level.
robot = getRobot()
if robot.frame.y < 400:
showBoxEvent.y = 550
else:
showBoxEvent.y = 50
else:
showBoxEvent.y = self.conversation.speechBoxYPos
#Set text
showBoxEvent.text = text
#Set wheter box has more responses left
showBoxEvent.hasNext = hasNext
#Create the box on the next tick
self.addStepEvent(showBoxEvent)
#Add a close event if requested
if closeOnTime:
timedEvent = TimedEvent(closeOnTime, self.speechBox.hide)
self.statusBoxHideEvent.newAction = timedEvent
elif closeOnMove:
onMoveEvent = OnMoveEvent(self.speechBox.hide)
self.statusBoxHideEvent.newAction = onMoveEvent
except:
calico.PrintLine(self.printTags[0].Key, traceback.format_exc())
[docs] def convEvent(self, action):
currentAction = self.conversation.threads[0].responses[-1].action
if not isinstance(currentAction, list):
if currentAction is None:
currentAction = []
else:
currentAction = [currentAction]
currentAction += [lambda: self.queue.add(action)]
self.conversation.threads[0].responses[-1].action = currentAction
[docs] def convPause(self):
self.pauseRobot(True)
[docs] def convStart(self):
self.pauseRobot(True)
self.conversation.autoUnpause = True
self.closeDialogOnMove = False
self.talk()
[docs] def convUnpause(self):
self.convEvent(lambda: self.pauseRobot(False))
[docs] def addResponse(self, portrait=None, text="", action=None, sticky=False):
"""
Convenience functions for adding responses to the conversation object.
Args:
portrait: The portrait to be show with the string. Can be a
Calico Picture object or a string representing the
location of an image file to be loaded. A default
Scribbler image is shown if None is passed.
text: The text to be printed (str).
sticky: Whether this text should be repeated when there is no
more conversation left.
"""
self.conversation.addResponse(portrait, text, action, sticky)
[docs] def setResponse(self, portrait=None, text="", action=None, sticky=False):
"""
Convenience functions for setting responses to the conversation object.
The only difference between adding and setting responses for the
conversation object is that, if you set a response for the conversation
object, it will immedidiately become the next response, skipping all
other responses, whereas if you add a response, a use will have to go
through all previous responses before getting the new one.
Args:
portrait: The portrait to be show with the string. Can be a
Calico Picture object or a string representing the
location of an image file to be loaded. A default
Scribbler image is shown if None is passed.
text: The text to be printed (str).
sticky: Whether this text should be repeated when there is no
more conversation left.
"""
self.conversation.setResponse(portrait, text, action, sticky)
[docs] def clearResponses(self):
"""Clears all responses from the conversation object."""
self.conversation.response=[]
[docs] def addThread(self, thread):
self.conversation.addThread(thread)
return thread
##########################
# Add-To-World Functions #
##########################
#Following Functions should be added to createLevel()
#Functions can be broken into different categories
#Note the order objects are made determines z Depth
#1. Setting background
#2. Creates static objects (walls, non-moving items)
#3. Creates start and end pads (glorified event pads)
#4. Create robot(s)
#5. Create dynamic moving objects (NPCs, hazards, moving items)
#6. Creaet fog
#Background
[docs] def setBackground(self,back):
"""
Sets a background image.
Args:
back: String pointing towards an image file (jpg, png).
Return:
A reference to the background image, or None if the function was
unsuccessful.
"""
if back!=None:
backPicture=LRC_Background(back)
backPicture.draw(self.worldFrame)
#Might be buggy. TODO Test. JH
#backPicture.stackOnBottom()
return backPicture
[docs] def setForeground(self,fore):
"""
Sets a foreground image.
Should probably not be used, as it may severly hurt performance.
Args:
fore: String pointing towards an image file (jpg, png).
Return:
A reference to the foreground image, or None if the function was
unsuccessful.
"""
if fore!=None:
forePicture=LRC_Background(fore)
forePicture.draw(self.worldFrame)
#forePicture.stackOnTop()
return forePicture
#Static/Dynamic Objects
[docs] def addObstacle(self, ob, vis=True):
"""
Helper function for adding static obstacles.
Args:
ob: Shape to be added as a static objec to the simulator.
Return:
A reference to the added shape.
"""
#print("HERE")
if isinstance(ob, Group):
for shape in ob.items:
self.addObstacle(shape)
else:
if not vis:
ob.visible = False
ob.draw(self.worldFrame)
ob.addToPhysics()
#self.sim.window.draw(ob)
ob.bodyType="static"
return ob
[docs] def addDecor(self, posOrOb, ob=None, vis=True):
pos = None
# TODO JH: The only reason for this admitadly bizar construction
# is that the Picture class in Calico is the only one that doesn't
# take some form of positioning as an argument.
# Refactering Calico slightly would allow us to do away with most of
# this stuff.
if isinstance(posOrOb, tuple):
pos = posOrOb
else:
assert ob is None
ob = posOrOb
if not vis:
ob.visible = False
if pos is not None:
ob.moveTo(pos[0], pos[1])
ob.draw(self.worldFrame)
return ob
[docs] def addActor(self, actor):
"""
Adds an actor to the world.
Args:
actor: The actor to be added.
Return:
A reference to the added actor.
"""
actor.draw(self)
#if actor.speechBubble:
# self.npcList.append(actor)
return actor
[docs] def quickActor(self, x, y, base, **kwargs):
"""
Convenience function for adding an actor to the world.
QuickActor will create an actor based on the 'base' that is passed to
the function. If the base is a string, it is assumed that you wish to
create a picture actor, with the string being a directory and filename.
If the base is a list, it is assumed you want to create a Sprite actor,
where every element in the list is a string pointing to a directory and
filename, each becoming a frame for the Sprite. If the base is a shape,
it is assumed you want to create a Shape actor, with the Shape being the
body of the actor.
Args:
x: The x position for the actor.
y: The y position for the actor.
base: The base on which the actor should be created.
kwargs: Keyword arguments passed direclty to the actor.
Return:
A reference to the created actor.
"""
if "dynamic" in kwargs:
if kwargs["dynamic"] and "bodyType" not in kwargs:
kwargs["bodyType"] = "dynamic"
if "bodyType" not in kwargs:
kwargs["bodyType"] = "static"
#if isinstance(base, str):
# return self.addActor(PictureActor((x, y), base, None, **kwargs))
#elif hasattr(base, "__iter__"):
return self.addActor(SpriteActor((x, y), base, None, **kwargs))
#else:
# return self.addActor(ShapeActor((x, y), base, **kwargs))
## def quickDecor(self, x, y, base, **kwargs):
## if isinstance(base, str):
## # print(base[-4:])
## if base[-4:] == ".png":
## base = makePic(base)
## else:
## base = makeText(base)
## if "color" in kwargs:
## base.color = kwargs["color"]
## if "rotation" in kwargs:
## base.rotateTo = kwargs["rotation"]
## if "scale" in kwargs:
## base.scaleTo = kwargs["scale"]
## base.moveTo(x, y)
## self.addDecor(base)
## return base
[docs] def createRobotStart(self, x, y, a):
self.createStartPad(x, y)
self.createRobot(x,y,a)
[docs] def quickItem(self, x, y, base, description=None, **kwargs):
"""
Convenience function that sets a description and pickup equal to True.
Args:
x: The x position for the actor.
y: The y position for the actor.
base: The base on which the actor should be created.
description: The description that is returned when the item is
printed.
kwargs: Keyword arguments passed direclty to the actor.
Return:
A reference to the created actor.
"""
if description:
kwargs["description"] = description
if "pickup" not in kwargs:
kwargs["pickup"] = True
return self.quickActor(x, y, base, **kwargs)
[docs] def quickLock(self, x, y, base, key, action,
failMsg="The key does not fit.", **kwargs):
"""
Convenience function creates a lock, based on a key and an action.
Args:
x: The x position for the actor.
y: The y position for the actor.
base: The base on which the actor should be created.
key: The key necessary to use this lock.
action: The action that is executed whent he key fits the lock.
failMsg: The message that is displayed when the key does not fit
the lock.
kwargs: Keyword arguments passed direclty to the actor.
Return:
A reference to the created actor.
"""
if base is None: base = makeCircle(45, "gold")
debug = False
if "debug" in kwargs: debug = kwargs["debug"]
kwargs["interact"] = LockEvent(key, action, failMsg, debug)
return self.quickActor(x, y, base, **kwargs)
#Platforms
[docs] def createEndPad(self,x,y,locked=False):
"""
Creates an end-pad at the indicated location.
Args:
x: The x-coordinate of the end-pad.
y: The y-coordinate of the end-pad.
Return:
A reference to the end pad.
"""
self.endPadLocked=locked
endPad=Platform((x,y),
35,
IMAGE_PATH+"endPlat.png",
collision=self.endCollide,
debug=False)
self.addActor(endPad)
return endPad
[docs] def createStartPad(self,x,y):
"""
Creates a start-pad at the indicated location.
Args:
x: The x-coordinate of the start-pad.
y: The y-coordinate of the start-pad.
Return:
A reference to the start pad.
"""
startPad=Platform((x,y),
1,
IMAGE_PATH+"startPlat.png",
separation=self.startRingCollideSep,
debug=False)
self.addActor(startPad)
return startPad
#Robot
[docs] def createRobot(self,x,y,rotation):
"""
Creates a robot at the indicated location.
Args:
x: The x-coordinate of the robot.
y: The y-coordinate of the robot.
rotation: The rotation of the robot.
"""
try:
#self.robots.append(makeRobot("SimScribblerRounded", self.sim))
self.robots.append(makeRobot("SimScribbler", self.sim))
except Exception as e:
if "Unable to make robot" in e.message:
print("Rounded Scribbler not found, reverting back to square Scribbler")
self.robots.append(makeRobot("SimScribbler", self.sim))
else:
raise
shapes = self.sim.window.canvas.shapes
shapes.RemoveAt(shapes.Count-1)
#self.sim.window.canvas.shapes
self.robots[-1].frame.draw(self.worldFrame)
self.robots[-1].frame.outline=makeColor(0,0,0,0)
self.robots[-1].frame.body.Friction=0.15
self.robots[-1].setPose(x,y,rotation)
#Fog
[docs] def createFog(self):
"""
Creates a 'fog-of-war' for the simulator.
Return:
A reference to the fog-of-war image.
"""
fog=Picture(IMAGE_PATH+"fog.png")
#fog.fill.alpha=245
fog.draw(self.robots[0].frame)
fog.move(-fog.height/2,-fog.width/2)
fog.rotate(-90)
return fog
##########################
#### Event Functions #####
##########################
[docs] def waitForStep(self):
"""
Blocks until one iteration of events has been processed.
Note: Can not be called in sync with calico thread.
"""
wait = WaitEvent()
self.addStepEvent(wait)
while not wait.finished:
pass
[docs] def addStepEvent(self, event):
"""
Add an event to the level thread. Events should be functions.
"""
if event not in self.toAdd:
self.toAdd.append(event)
[docs] def addStartEvent(self, event):
"""
Add an event to the start events.
"""
if event not in self.startEvents:
self.startEvents.append(event)
[docs] def addEndEvent(self, event):
"""
Add an event to the end events.
"""
if event not in self.endEvents:
self.endEvents.append(event)
[docs] def launchEndEvents(self):
"""
Called when the level ends, for whathever reason.
"""
try:
for event in self.endEvents:
event()
except:
print(traceback.format_exc())
[docs] def launchStartEvents(self):
"""
Called when the robot leaves the starting plate.
"""
try:
for event in self.startEvents:
event()
except:
print(traceback.format_exc())
[docs] def levelThread(self):
"""
Experiment with an event based level thread.
"""
try:
for event in self.toAdd:
#if event not in self.stepEvents: # JH: Necessary?
self.stepEvents.append(event)
del self.toAdd[:]
self.stepEvents = [event for event in self.stepEvents if event()]
except:
print(traceback.format_exc())
[docs] def clearStepEvents(self):
"""Clears all events."""
del self.toAdd[:]
del self.stepEvents[:]
#########################################
#Base Collision and Separation Functions#
#########################################
[docs] def startRingCollideSep(self, myfixture, otherfixture):
"""
Callback for one the robot leaves the starting pad.
Args:
myfixture: Should be the starting pad.
otherfixture: Should be the robot.
"""
r=getRobot()
sepTriggeredByRobot = otherfixture.Body.UserData==r.frame.body.UserData
if r is None: return
if not self.runTimer and sepTriggeredByRobot:
#bring down the briefing it is up
#self.briefScreen.visible=False
self.hidePanel1()
self.runTimer=True
self.startTime=time()
self.launchStartEvents()
[docs] def endCollide(self,myfixture, otherfixture, contact):
"""
Callback for one the robot reaches the end-pad.
Args:
myfixture: Should be the end-pad.
otherfixture: Should be the robot.
contact: The Farseer Contact holding contact information.
Returns:
True. Returning anything else may cause weird bugs.
"""
self.tempContact=contact
r=getRobot()
if r!=None and not self.endPadLocked:
if otherfixture.Body.UserData==r.frame.body.UserData:
if not isnan(r.frame.getX()) and not isnan(r.frame.getY()):
self.setGameOver(1)
return True
[docs] def interactCollide(self, myfixture, otherfixture, contact):
"""
Collision function for objects that want to be able to interact.
Args:
myfixture: The object that registered the collision callback.
otherfixture: The object that collided with us.
contact: The Farseer Contact holding contact information.
Returns:
True. Returning anything else may cause weird bugs.
"""
r=getRobot()
if r is None: return True
if otherfixture.Body.UserData==r.frame.body.UserData:
self.interactContactList.append((myfixture.Body.UserData, contact))
return True
[docs] def interactSeparate(self, myfixture, otherfixture):
"""
The separation function for object that want to be able to interact.
While not as important as the collision function, registering the
seperation callback OnSeperation, can speed up the simulation by
throwing away unnecessary contacts.
Args:
myfixture: The object that registered the separation callback.
otherfixture: The object that separated from us.
"""
try:
r=getRobot()
if r is None: return
if otherfixture.Body.UserData==r.frame.body.UserData:
self.interactContactList=cleanContactList(self.interactContactList)
except:
print(traceback.format_exc())
[docs] def pickupCollide(self, myfixture, otherfixture, contact):
"""
Collision function for objects that want to be able to picked up.
Args:
myfixture: The object that registered the collision callback.
otherfixture: The object that collided with us.
contact: The Farseer Contact holding contact information.
Returns:
True. Returning anything else may cause weird bugs.
"""
try:
r=getRobot()
if r is None: return
if otherfixture.Body.UserData==r.frame.body.UserData:
self.pickupContactList.append((myfixture.Body.UserData, contact))
return True
except:
print(traceback.format_exc())
[docs] def pickupSeparate(self, myfixture, otherfixture):
"""
The separation function for object that want to be able to be picked up.
While not as important as the collision function, registering the
seperation callback OnSeperation, can speed up the simulation by
throwing away unnecessary contacts.
Args:
myfixture: The object that registered the separation callback.
otherfixture: The object that separated from us.
"""
try:
r=getRobot()
if r is None: return
if otherfixture.Body.UserData==r.frame.body.UserData:
self.pickupContactList=cleanContactList(self.pickupContactList)
except:
print(traceback.format_exc())
[docs] def deathCollide(self, myfixture, otherfixture, contact):
"""
Convience collision function you can install on custom hazards.
Args:
myfixture: The object that registered the collision callback.
otherfixture: The object that collided with us.
contact: The Farseer Contact holding contact information.
Returns:
True. Returning anything else may cause weird bugs.
"""
r=getRobot()
if r:
if otherfixture.Body.UserData==r.frame.body.UserData:
self.setGameOver(-1)
return True
##################
# Misc Functions #
##################
[docs] def checkVariableValue(self):
def fun():
for key in calico.manager.scope.GetVariableNames():
print(key, calico.manager.scope.GetVariable(key))
[docs] def safeMove(self, x, y, a):
while self.teleportState==1: pass
self.teleportState=1
self.addStepEvent(lambda: self._safeMove(x,y,a))
[docs] def safeMoveBlock(self, x, y, a):
self.safeMove(x, y, a)
while self.teleportState!=0:
pass
def _safeMove(self, x, y, a):
robot = getRobot()
if not robot:
return False
self.teleportState=0
robot.stop()
#The contacts do not like the robot suddenly teleporting. Clear all of them (they will be recreated).
del self.interactContactList[:]
del self.pickupContactList[:]
robot.setPose(x, y, a)
robot.frame.body.ResetDynamics()
disableContactList(robot.frame.body.ContactList)
self.teleportState=0
return False
#Possible fonts for Calico
#http://www.w3.org/TR/CSS2/fonts.html#generic-font-families