Source code for hazards

import sys,os

sys.path.append("../CommonFunctions")
from levelWindow import *
from math import *
from actors import * 
from utilityFunctions import *
from makeShapeFunctions import * 
from interactables import *
#used mostly to grabbing stage specific images
LEVEL_PATH = os.path.dirname(os.path.realpath(__file__))+"/"

[docs]class Hazard(object): """ Base object for all hazards. """ def __init__(self): """ Constructs a Hazards object. """ #: Indicates whether or not this hazard is disabled. self.run=False
[docs] def disable(self): """ Disables the hazard. The effect of disabling the hazard is implementation dependend per hazard. """ self.run=False
[docs] def isDisabled(self): """ Indicates whether this hazard is enabled or not. Return: Returns True if this hazard is disabled, False otherwise. """ return not self.run
#Can probably combine FireBall and Thwomp eventually
[docs]class FireBall(Hazard,SpriteActor): """ A fireball that moves back and forth. """ def __init__(self, start, end, costume, collision, scale=1, speed=10, run=True, debug=False, startLoc=None, bounce=True): """ Creates a FireBall object. Args: start: The starting position of the fireball. end: The end position of the fireball. costume: Integer indicating the initial costume of the fireball. The customes come in this order: up, down, left, right. collision: The collision function called when something hits the fireball. scale: The scale of the fireball. speed: The speed of the fireball. run: Whether or not the fireball should immediately active. debug: Whether debug visuals should be enabled (shows the hitbox). startLoc: Set if the starting position should be different from the patroll starting point. bounce: If True, the fireball will move back-and-forth. If False, the fireball will respawn at the start position when it reaches the end position. """ # scale, collision):lowerBound, upperBound, leftBound, rightBound, # collision, costume=0): files = [LEVEL_PATH+"fireballUp.png", LEVEL_PATH+"fireballDown.png", LEVEL_PATH+"fireballLeft.png", LEVEL_PATH+"fireballRight.png"] SpriteActor.__init__(self, start, files , makeColCircle(35, "dynamic"), collision=collision, debug=debug,scale=scale) self.description = "hazard: fire " + str(start) self.start=start self.end=end self.speed=speed self.run=run self.direct=[self.end[0]-self.start[0],self.end[1]-self.start[1]] #direction of fireball self.vel=norm(self.direct) #normalize the direction to obtain a reasonable baseline velocity self.changeCostume(costume) self.bounce=bounce if startLoc != None: self.moveTo(startLoc[0],startLoc[1])
[docs] def isDisabled(self): """ Indicates whether this hazard is enabled or not. Return: Returns True if this hazard is disabled, False otherwise. """ return not self.run
[docs] def disable(self): """ Disables the hazard. Prevents movement and hides the fireball. """ self.run=False self.shape.body.LinearVelocity = Vector(0,0) self.hide()
[docs] def step(self): """ Move the fireball one step. Return: Always returns True. """ pastEnd=False if self.run: #see if fireball has moved beyond the end point #based on the equation: self.shape.location=self.start + t*self.direction #t ranges from 0 (at start) to 1 (at end) #to find t, and whether the shape is beyond the end point #t=(self.location-self.start)/self.direction if self.direct[0]==0: #avoid dividing by 0 if ((self.shape.y-self.start[1])/self.direct[1]) >= 1: pastEnd=True elif self.direct[1]==0: #avoid divinding by 0 if ((self.shape.x-self.start[0])/self.direct[0]) >= 1: pastEnd=True else: if ((self.shape.x-self.start[0])/self.direct[0]) >=1 and ((self.shape.y-self.start[1])/self.direct[1]) >=1: pastEnd=True if pastEnd: if self.bounce: #self.speed=-1*self.speed #changes direction of velocity temp=self.start self.start=self.end self.end=temp self.direct[0]*=-1 self.direct[1]*=-1 self.vel[0]*=-1 self.vel[1]*=-1 if self.currentCostumeIndex==0: self.changeCostume(1) elif self.currentCostumeIndex==1: self.changeCostume(0) elif self.currentCostumeIndex==2: self.changeCostume(3) elif self.currentCostumeIndex==3: self.changeCostume(2) else: #wrap around screen self.moveTo(self.start[0],self.start[1]) else: self.shape.body.LinearVelocity = Vector(self.speed*self.vel[0], self.speed*self.vel[1]) return True
#JH: I'm trying out a couple of modification to fireball which are not going to #be backward compatible, hence a new class for now.
[docs]class FireBall2(Hazard,SpriteActor): """ Experimental fireball that can use multiple waypoints. """ def __init__(self, waypoints=[], costume=0, collision=None, scale=1, speed=10, run=True, debug=False, startLoc=None, bounce=True): """ Creates a FireBall object. Args: waypoints: List of waypoints that fireball will follow. costume: Integer indicating the initial costume of the fireball. The customes come in this order: up, down, left, right. collision: The collision function called when something hits the fireball. scale: The scale of the fireball. speed: The speed of the fireball. run: Whether or not the fireball should immediately active. debug: Whether debug visuals should be enabled (shows the hitbox). startLoc: Set if the starting position should be different from the patroll starting point. bounce: If True, the fireball will move back-and-forth. If False, the fireball will respawn at the start position when it reaches the end position. """ if collision is None: collision = self.defaultCollide if startLoc != None: start = startLoc else: start=waypoints[0] SpriteActor.__init__(self, start, LEVEL_PATH+"fireballUp.png" , makeColCircle(35, "dynamic"), collision=collision, debug=debug,scale=scale) self.description = "hazard: fire " + str(start) self.currentWaypoint=0 self.waypoints=waypoints self.speed=speed self.direction=1 self.run=run self.changeCostume(costume) self.atEnd="bounce" self.moveEvent = None
[docs] def defaultCollide(self, myfixture, otherfixture, contact): """ Default collision function that sets a game over if the robot touches the fireball. Args: myfixture: Fixture of the fireball. otherfixture: Fixture of whatever we collided with. contact: The contact of the collision. """ if self.levelWindow is None: return True robot = getRobot() if robot is None: return True if otherfixture.Body.UserData==robot.frame.body.UserData: self.levelWindow.setGameOver(-1) return True
[docs] def reverse(self): """ Causes the fireball to start moving in the opposite direction. """ self.direction*=-1
[docs] def warp(self): """ Warps the fireball back to the starting point. """ point = self.waypoints[self.currentWaypoint] self.moveTo(point[0], point[1])
[docs] def pause(self): """ Temporarily stops the fireball from moving. """ if self.moveEvent is None: return self.moveEvent.pause = True
[docs] def unpause(self): """ Causes the fireball to continue to move if it was paused. """ if self.moveEvent is None: return self.moveEvent.pause = False
[docs] def stop(self): """ Stops the fireball by deleting its move event. Can only be continued by registering a new move event. """ if self.moveEvent is None: return self.moveEvent.remove = True
[docs] def start(self): """ Causes the fireball to start moving. """ if self.levelWindow is None: return self.currentWaypoint += self.direction if self.currentWaypoint >= len(self.waypoints) or self.currentWaypoint < 0: if self.atEnd == "bounce": self.reverse() self.currentWaypoint += self.direction*2 elif self.atEnd == "circle": self._wrapWaypoint() elif self.atEnd == "warp": self._wrapWaypoint() self.warp() elif self.atEnd == "stop": return else: print("Warning: Unknown atEnd action") point = self.waypoints[self.currentWaypoint] dx=point[0]-self.getX() dy=self.getY() - point[1] if dx != 0 or dy != 0: angle = getAngleVector(dx, dy) self.rotateTo(angle) #print("line 169 (hazards.py): current location:", self.getX(), self.getY(), "target location:", point[0], point[1]) self.moveEvent = MoveToEvent(self, point[0], point[1], self.speed, self.start) self.levelWindow.addStepEvent(self.moveEvent)
def _wrapWaypoint(self): """ Wraps the waypoint counter if it gets outside of the bounds of the waypoint list. """ if self.currentWaypoint < 0: self.currentWaypoint = len(self.waypoints)-1 elif self.currentWaypoint >= len(self.waypoints): self.currentWaypoint = 0
[docs] def draw(self, levelWindow): """ Draws the fireball to the level. Args: levelWindow: The :class:`.LevelWindow` to draw to. """ SpriteActor.draw(self, levelWindow) if self.run: self.start()
[docs]class Missile(Hazard, SpriteActor): """ A missle that follows a set of waypoints, and explodes on contact. """ def __init__(self, waypoints=[], speed=10, run=True, startLoc=None, atEnd="bounce", **kwargs): """ Creates a Missile object. Args: waypoints: List of waypoints that missile will follow. speed: The speed of the missile. run: Whether or not the missile should immediately active. startLoc: Set if the starting position should be different from the patroll starting point. atEnd: If set to 'bounce' will cause the missle to move back-and-forth, if set to 'warp' will cause the missle to warp back to the start, if set to 'circle' will move to the start when the end is reached, if set to 'stop' the missle will be disabled after reaching the end, and if set to 'custom' a callback function will be called to determine what happens. kwargs: All other keyword arguments will be passed to the parent :class:`.SpriteActor`. """ if "collision" not in kwargs: kwargs["collision"] = self.defaultCollide if startLoc != None: start = startLoc else: start=waypoints[0] frames = [LEVEL_PATH+"RocketSimple.png", LEVEL_PATH+"RocketSimpleFire.png", LEVEL_PATH+"RocketSimpleMoreFire.png"] hitbox = makeColCircle(35, "dynamic") SpriteActor.__init__(self, start, frames, hitbox, **kwargs) self.description = "hazard: fire " + str(start) self.currentWaypoint=0 self.waypoints=waypoints self.speed=speed self.direction=1 self.run=run self.atEnd=atEnd self.moveEvent = None self.customTrackingEvent = None
[docs] def defaultCollide(self, myfixture, otherfixture, contact): """ Default collision function that sets a game over if the robot touches the missle. Args: myfixture: Fixture of the missle. otherfixture: Fixture of whatever we collided with. contact: The contact of the collision. """ if self.levelWindow is None: return True robot = getRobot() if robot is None: return True if otherfixture.Body.UserData==robot.frame.body.UserData: self.levelWindow.setGameOver(-1) return True
[docs] def reverse(self): """ Causes the missile to start moving in the opposite direction. """ self.direction*=-1
[docs] def warp(self): """ Warps the missile back to the starting point. """ point = self.waypoints[self.currentWaypoint] self.moveTo(point[0], point[1])
[docs] def pause(self): """ Temporarily stops the fireball from moving. """ if self.moveEvent is None: return self.moveEvent.pause = True
[docs] def unpause(self): """ Causes the missile to continue to move if it was paused. """ if self.moveEvent is None: return self.moveEvent.pause = False
[docs] def stop(self): """ Stops the missile by deleting its move event. Can only be continued by registering a new move event. """ if self.moveEvent is None: return self.moveEvent.remove = True
[docs] def start(self): """ Causes the missile to start moving. """ if self.levelWindow is None: return self.currentWaypoint += self.direction if self.currentWaypoint >= len(self.waypoints) or self.currentWaypoint < 0: if self.atEnd == "bounce": self.reverse() self.currentWaypoint += self.direction*2 elif self.atEnd == "circle": self._wrapWaypoint() elif self.atEnd == "warp": self._wrapWaypoint() self.warp() elif self.atEnd == "custom": self.levelWindow.addStepEvent(self.customTrackingEvent) return elif self.atEnd == "stop": return else: print("Warning: Unknown atEnd action") point = self.waypoints[self.currentWaypoint] if len(point) == 3: point[2](self) dx=point[0]-self.getX() dy=self.getY() - point[1] if dx != 0 or dy != 0: angle = getAngleVector(dx, dy) self.rotateTo(angle) #print("line 169 (hazards.py): current location:", self.getX(), self.getY(), "target location:", point[0], point[1]) self.moveEvent = MoveToEvent(self, point[0], point[1], self.speed, self.start) self.levelWindow.addStepEvent(self.moveEvent)
[docs] def reset(self): """ Moves the missile back to the starting position. """ self.stop() self.currentWaypoint=0 self.warp() self.start()
def _wrapWaypoint(self): """ Wraps the waypoint counter if it gets outside of the bounds of the waypoint list. """ if self.currentWaypoint < 0: self.currentWaypoint = len(self.waypoints)-1 elif self.currentWaypoint >= len(self.waypoints): self.currentWaypoint = 0
[docs] def draw(self, levelWindow): """ Draws the missile to the level. Args: levelWindow: The :class:`.LevelWindow` to draw to. """ SpriteActor.draw(self, levelWindow) if self.run: self.start()
[docs]class Thwomp(Hazard,SpriteActor): """ A Mario Thwomp that moves up and down. """ def __init__(self, start, end, costume, collision, scale=1, upSpeed=10, downSpeed=20, run=True, debug=False, startLoc=None, bounce=True, onStep=None, hitbox=None): """ Creates a Thwomp object. Args: start: The starting position of the thwomp. end: The end position of the thwomp. costume: Integer indicating the initial costume of the thwomp. The customes come in this order: up, down. collision: The collision function called when something hits the thwomp. scale: The scale of the thwomp. upSpeed: The upward speed of the thwomp. downSpeed: The downward speed of the thwomp. run: Whether or not the fireball should immediately active. debug: Whether debug visuals should be enabled (shows the hitbox). startLoc: Set if the starting position should be different from the patroll starting point. bounce: If True, the fireball will move back-and-forth. If False, the fireball will respawn at the start position when it reaches the end position. onStep: Function called at every movement step of the thwomp. hitbox: A custom hitbox for the thwomp, useful if it scalled to a different size. """ # scale, collision):lowerBound, upperBound, leftBound, # rightBound, collision, costume=0): files = [LEVEL_PATH+"thwompUp.png", LEVEL_PATH+"thwompDown.png"] if hitbox is None: hitbox=makeColRec(90, 110, "dynamic") SpriteActor.__init__(self, start, files , hitbox, collision=collision, debug=debug,scale=scale) self.description = "hazard: thwomp " + str(start) self.start=start self.end=end self.upSpeed=upSpeed self.downSpeed=downSpeed self.run=run # direction of thwomp self.direct=[self.end[0]-self.start[0],self.end[1]-self.start[1]] # normalize the direction to obtain a reasonable baseline velocity self.vel=norm(self.direct) self.changeCostume(costume) self.bounce=bounce self.onStep = onStep self.pastEnd=False if startLoc != None: self.moveTo(startLoc[0],startLoc[1])
[docs] def isDisabled(self): """ Indicates whether this hazard is enabled or not. Return: Returns True if this hazard is disabled, False otherwise. """ return not self.run
[docs] def disable(self): """ Disables the hazard. Prevents movement and hides the thwomp. """ self.run=False self.shape.body.LinearVelocity = Vector(0,0) self.hide()
[docs] def step(self): """ Move the thwomp one step. Return: Always returns True. """ self.pastEnd=False if self.run: #see if fireball has moved beyond the end point #based on the equation: self.shape.location=self.start + t*self.direction #t ranges from 0 (at start) to 1 (at end) #to find t, and whether the shape is beyond the end point #t=(self.location-self.start)/self.direction if self.direct[0]==0: #avoid dividing by 0 if ((self.shape.y-self.start[1])/self.direct[1]) >= 1: self.pastEnd=True elif self.direct[1]==0: #avoid divinding by 0 if ((self.shape.x-self.start[0])/self.direct[0]) >= 1: self.pastEnd=True else: if ((self.shape.x-self.start[0])/self.direct[0]) >=1 and ((self.shape.y-self.start[1])/self.direct[1]) >=1: self.pastEnd=True if self.pastEnd: if self.bounce: #self.speed=-1*self.speed #changes direction of velocity temp=self.start self.start=self.end self.end=temp self.direct[0]*=-1 self.direct[1]*=-1 self.vel[0]*=-1 self.vel[1]*=-1 if self.currentCostumeIndex==0: self.changeCostume(1) elif self.currentCostumeIndex==1: self.changeCostume(0) elif self.currentCostumeIndex==2: self.changeCostume(3) elif self.currentCostumeIndex==3: self.changeCostume(2) else: #wrap around screen self.moveTo(self.start[0],self.start[1]) else: if self.currentCostumeIndex==0: self.shape.body.LinearVelocity = Vector(self.upSpeed*self.vel[0], self.upSpeed*self.vel[1]) else: self.shape.body.LinearVelocity = Vector(self.downSpeed*self.vel[0], self.downSpeed*self.vel[1]) else: self.shape.body.LinearVelocity = Vector(0, 0) if self.onStep: self.onStep(self) return True
[docs]class Grinder(Hazard,SpriteActor): """ A spinning blade that may follow a circular path. """ def __init__(self, start, collision, scale=1, rotationSpeed=1, debug=False, orbit=False, orbitCenter=(0,0), orbitRadius=100, orbitStep=0.25, currentAngle=0,run=True): """ Creates a Grinder object. Args: start: The starting position of the grinder. collision: Function called when something hits the grinder. scale: The scale of the grinder. rotationSpeed: The rotational speed of the grinder. debug: Whether debug visuals should be enabled (shows the hitbox). orbit: Whether or not the grinder should move in a circle. orbitCenter: The center around which the grinder moves. orbitRadius: The distance from the center around which the grinder moves. orbitStep: The speed at which the grinder moves. currentAngle: The angle at which the grinder starts its movement. run: Whether or not the grinder should be immediately active. """ files = LEVEL_PATH+"grinder.png" # JH: Why are there two function calles here that do the same? if rotationSpeed<0: SpriteActor.__init__(self, start, files, makeColCircle(scale*210, "dynamic"), collision=collision, debug=debug,scale=scale) else: SpriteActor.__init__(self, start, files, makeColCircle(scale*210, "dynamic"), collision=collision, debug=debug,scale=scale) self.description = "hazard: grinder " + str(start) self.start=start self.rotationSpeed=rotationSpeed self.run=run self.orbit=orbit self.orbitCenter=orbitCenter self.orbitRadius=orbitRadius self.orbitStep=orbitStep self.currentAngle=currentAngle
[docs] def isDisabled(self): """ Indicates whether this hazard is enabled or not. Return: Returns True if this hazard is disabled, False otherwise. """ return not self.run
[docs] def disable(self): """ Disables the hazard. Prevents movement and hides the grinder. """ self.run=False self.shape.body.LinearVelocity = Vector(0,0) self.hide()
[docs] def step(self): """ Move the grinder one step. Return: Always returns True. """ if self.run: self.shape.body.AngularVelocity=self.rotationSpeed if self.orbit: self.shape.moveTo(cos(self.currentAngle)*self.orbitRadius+self.orbitCenter[0],sin(self.currentAngle)*self.orbitRadius+self.orbitCenter[1]) self.currentAngle+=self.orbitStep#2*pi*self.orbitRadius*(self.orbitStep/(2*pi)) self.currentAngle %= (2*pi) return True
[docs]class Boulder(SpriteActor): """ A boulder. Class looks disfunctional. """ def __init__(self, start, collision, scale=1, speed=1, debug=False, obstructs=True): """ Creates a Boulder object. Args: start: The starting position of the boulder. collision: Function called when something hits the boulder. scale: The scale of the boulder. speed: The speed of the boulder. debug: Whether debug visuals should be enabled (shows the hitbox). obstructs: Whether or not the boulder should obstruct movement. """ # scale, collision):lowerBound, upperBound, leftBound, rightBound, # collision, costume=0): files = LEVEL_PATH+"boulder.png" SpriteActor.__init__(self, start, files, makeColCircle(scale*55, "dynamic"), collision=collision, debug=debug, scale=scale, obstructs=obstructs) self.description = "boulder " + str(start) self.start=start self.speed=speed
[docs] def step(self): """ Should perform one step of the boulder, but the self.orbit attribute is not an actual attribute of this boulder, so this function would usually crash. """ if not self.orbit: self.shape.body.AngularVelocity=self.speed
#def draw(self,levelWindow): # PictureActor.draw(levelWindow) #################NPC CLASSES################### #Might be moved to another file npc.py######### ## ############################################ #self.statusBox.closeOnTime=True #self.statusBox.timer=10 #TODO: More robust follower AI #The simpleFollowerAI can be fooled b/c it will follow #the robot's previous location and not necessary make a #direct line to the robot. Here's one way to make a more #robustFollowerAI #Step1: Break the world into grids. Grid size would depend #on the physical dimensions of the follower AI on screen #Step2: Create a vertex map based on the gridded world but #exclude grids that contain obstacles b/c rationally you #can't move onto those obstacles #Step3a: Do a simple AStar or route finding to find the grids/vertices #that lead you to the robot. Periodically recheck the route b/c #the robot will be moving. #Step3b: Instead of doing a route finding algorithm and periodically #rechecking b/c the robot is moving just do sometype #of dynamic route finding. #Very simple AI that monitors the past locations of the robot #and simple follows those past locations to follow the robot. #TODO: Make a separate called simpleFollowerAI and then #any character like the Kuka could inherit the step and follow #procedures
[docs]class Kuka(SpriteActor): """ A robot enemy that monitors the past locations of the robot and simple follows those past locations to follow the robot. """ def __init__(self, start, hitMessage=None, hitPause=20,hitAction=None, speed=10,obstructs=False, visible=True,bodyType="dynamic", scale=1, debug=False): """ Constructs a Kuka object. Args: start: The starting position of the Kuka. hitMessage: A message shown when the Kuka is hit. hitPause: The amount of time the Kuka waits after it is hit. hitAction: Function executed when the Kuka is hit. speed: The speed of the Kuka. obstructs: Whether or not the Kuka obstructs pathing. visible: Whether or not the Kuka is visible. bodyType: Either 'static' or 'dynamic'. scale: The scale of the Kuka Sprite. debug: Whether debug visuals should be enabled (shows the hitbox). """ # scale, collision):lowerBound, upperBound, leftBound, rightBound, #collision, costume=0): files = IMAGE_PATH+"kuka.png" SpriteActor.__init__(self, start, files , makeColRec(50, 70, "dynamic"), visible=visible,collision=self.hitCollide, separation=self.hitSeparation, debug=debug, scale=scale,obstructs=obstructs, bodyType=bodyType) self.run=False self.level=getLevelObject() self.robot=self.level.robots[0] self.wayPoints=[] self.minPointDist=10 self.atEnd=True self.start=None self.stop=None self.speed=speed self.distance=None self.direction=None self.endIndex=0 self.hitPauseCounter=0 self.hitPause=hitPause self.hitMessage=hitMessage self.hitAction=hitAction self.damaged=False # flag used to make sure Kuka only uses the portal once when it steps on # it self.onPortal=False
[docs] def stopMotion(self): """ Stops the movement of the Kuka. """ self.shape.body.LinearVelocity = Vector(0,0)
[docs] def hitFlash(self): """ Function causes the Kuka to blink. """ if self.visible(): self.hide() else: self.show()
[docs] def findClosestWayPoint(self,p): """ Finds the closest waypoint to the provided position. Args: p: The point from which we to find the closest waypoint. Return: The waypoint closest to the point p. """ #print("Closest to ",p) minDist=9999 minIndex=0 for i in range(len(self.wayPoints)): d=dist(self.wayPoints[i],p) if d<minDist: minDist=d minIndex=i #print("minDistance is",minDist) #print("closest way point is",self.wayPoints[minIndex]) return minIndex
[docs] def hitSeparation(self,myfixture,otherfixture): """ Records when the Kuka leaves a portal. Args: myfixture: The fixture of the Kuka. otherfixture: The fixture of the other object. """ try: if isinstance(otherfixture.Body.UserData,Portal): self.onPortal=False except: print("error with kuka separation")
[docs] def hitCollide(self, myfixture, otherfixture, contact): """ The collision function of the Kuka. Args: myfixture: The fixture of the Kuka. otherfixture: The fixture of the other object. contact: The contact of the collision. """ r=getRobot() if r: if otherfixture.Body.UserData==r.frame.body.UserData: self.level.setGameOver(-1) else: try: if isinstance(otherfixture.Body.UserData,Hazard): if not otherfixture.Body.UserData.isDisabled(): try: otherfixture.Body.UserData.disable() otherfixture.Body.UserData.moveTo(-100,-100) except: print("Hit hazard doesn't have disable function.") hitFlashEvent = RepeatedActionEvent(0.2,4,self.hitFlash) self.level.addStepEvent(hitFlashEvent) self.hitPauseCounter=self.hitPause self.damaged=True if self.hitMessage: self.level.printToSpeechBox(self.hitMessage, self.getAvatar()) if self.hitAction: self.hitAction() #I'm thinking of giving all interactables a npcAction function where #if an npc hits them it can pass itselfs (self) or simply invoke the #action elif isinstance(otherfixture.Body.UserData,Portal): if not self.onPortal: portal=otherfixture.Body.UserData self.moveTo(portal.end[0],portal.end[1]) #self.wayPoints=[] self.endIndex=self.findClosestWayPoint(portal.end) self.atEnd=True self.stopMotion() self.onPortal=True else: pass except: print("Error with Kuka's hit collide.") return True
[docs] def recordWayPoints(self): """ Records the current position of the robot as a waypoint. """ newPoint=[self.robot.frame.x,self.robot.frame.y] #print(newPoint) if len(self.wayPoints)==0: self.wayPoints.append(newPoint) else: #print(dist(self.wayPoints[-1],newPoint)) if dist(self.wayPoints[-1],newPoint)>5: self.wayPoints.append(newPoint)
[docs] def step(self): """ Move the Kuka one step. Return: Returns False if the game is over, True otherwise. """ if self.run: #start waypoint recording self.recordWayPoints() #if gameOver then return False and stop if self.level.gameOver!=0: self.shape.body.LinearVelocity = Vector(0,0) return False #print("wayPOints",self.wayPoints) #potentially pick new start and stop points if self.atEnd: #print("wayPoints",self.wayPoints) if len(self.wayPoints)<=0: return True else: if self.endIndex<len(self.wayPoints): self.start=[self.getX(),self.getY()] self.end=self.wayPoints[self.endIndex] self.endIndex+=1 #self.start=self.wayPoints[self.endIndex-1] self.atEnd=False self.distance=[self.end[0]-self.start[0],self.end[1]-self.start[1]] #distance to end of x and y #print("Distance",self.distance) if fabs(self.distance[0])<0.01 and fabs(self.distance[1])<0.01: #avoid dividing by 0 self.atEnd=True self.direction=[0,0] else: self.direction=norm(self.distance) #normalize the distance to get a vector of the direction to go #print("Direction",self.direction) #move from start to stop else: loc=[self.getX(),self.getY()] #loc=[self.getX(),self.getY()] #print("Going from ",self.start," to ",self.end) if fabs(self.distance[0])<0.01: #avoid dividing by 0 if (self.getY()-self.start[1])/self.distance[1] >= 1: 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 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.distance[0] >=1 and (self.getY()-self.start[1])/self.distance[1] >=1: self.atEnd=True if self.atEnd: #print("Actual position is ",loc) #print("Dist from start to actual loc ",dist(self.start,loc)) self.shape.body.LinearVelocity = Vector(0,0) else: self.shape.body.LinearVelocity = Vector(self.speed*self.direction[0], self.speed*self.direction[1]) if self.hitPauseCounter>0: self.hitPauseCounter-=1 self.shape.body.LinearVelocity = Vector(0,0) if self.hitPauseCounter==0: self.level.speechBox.hide() return True
[docs]class Boo(SpriteActor): """ The Mario Ghost Boo. Boo is a hazard that moves straight towards the player, ignoring intervening terrain. Class is currently defunct, because speech is not a valid argument for a SpriteActor. """ def __init__(self, start, costume, collision, scale=1, run=True, debug=False, speech=True): """ Constructs a Boo object. Args: start: The starting position of Boo. costume: Interger indicating the starting position of Boo. The costumes come in the following order: left, right. collision: The collision function called when something hits Boo. scale: The scale of Boo's sprite. run: Whether or not Boo should be immediately active. debug: Whether debug visuals should be enabled (shows the hitbox). speech: No longer a valid argument. """ # scale, collision):lowerBound, upperBound, leftBound, rightBound, # collision, costume=0): files = [LEVEL_PATH+"booLeft.png", LEVEL_PATH+"booRight.png"] # FIXME: JH - speech is not a valid argument for SpriteActor, so those # code will fail if executed. SpriteActor.__init__(self, start, files , makeColCircle(scale*100,"dynamic"), collision=collision, debug=debug, scale=scale, speech=speech) self.changeCostume(costume) self.run=run
[docs] def step(self): """ Moves Boo one step. Return: Always returns True. """ #speed of ghost s=15 if self.run: rX=self.levelWindow.robot.frame.getX() rY=self.levelWindow.robot.frame.getY() bX=self.getX() bY=self.getY() if rX-bX==0: self.move(copysign(s,rX-bX),copysign(s,rY-bY)) else: #slope of line between robot and ghost m=(rY-bY)/(rX-bX) #distance between robot and ghost D=sqrt(pow(rX-bX,2) + pow(rY-bY,2)) #distance from robot that ghost will be placed at d=D-s #solve the equation #d^2=y^2 + x^2 where y and x are the distance to the #point the ghost will be placed at #ans should produce two values which will be #the x (run) value ans=quadFormula(pow(m,2)+1,0,-pow(d,2)) if ans[0]==2: nX1=rX+ans[1][0] nX2=rX+ans[1][1] nY1=rY+ans[1][0]*m nY2=rY+ans[1][1]*m #there are two possible solutions to the equation #we want the one closer to the ghost pD1=sqrt(pow(bX-nX1,2)+pow(bY-nY1,2)) pD2=sqrt(pow(bX-nX2,2)+pow(bY-nY2,2)) if pD1<pD2: self.moveTo(nX1,nY1) else: self.moveTo(nX2,nY2) if self.getX()<self.levelWindow.robot.frame.getX() and self.currentCostumeIndex==0: self.changeCostume(1) elif self.getX()>=self.levelWindow.robot.frame.getX() and self.currentCostumeIndex==1: self.changeCostume(0) return True
''' def nextWayPoint(self): minDist=9999 minIndex=None kLoc=[self.getX(),self.getY()] rLoc=[self.robot.frame.x,self.robot.frame.y] for i in range(len(self.wayPoints)): d=dist(self.wayPoints[i],kLoc)+dist(self.wayPoints[i],rLoc) if d<minDist and i!=self.currentGoal: minDist=d minIndex=i return minIndex def step3(self): if self.run: #if gameOver then return False and stop if self.level.gameOver!=0: self.shape.body.LinearVelocity = Vector(0,0) return False #start waypoint recording self.recordWayPoints() #find closest waypoint self.currentGoal=self.nextWayPoint() self.end=self.wayPoints[self.currentGoal] self.start=[self.getX(),self.getY()] #find distance and then normalize to a 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) #normalize the distance to get a vector of the direction to go #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]) self.shape.body.LinearVelocity = Vector(self.speed*self.direction[0], self.speed*self.direction[1]) return True else: return True def closestWayPoint(self): minDist=9999 minIndex=None loc=[self.getX(),self.getY()] for i in range(len(self.wayPoints)): d=dist(self.wayPoints[i],loc) if d<minDist: minDist=d minIndex=i print("minIndex ",minIndex) return minIndex '''