def hasNoFreeMovementTo(self, target, source=None): """Check if the character has NOT free straight movement to a destination point. If this is True, the character can't move directly on the vector that link his current position to the target due to a collision that will be raised. @target: a point coordinate, goal of the movement @source: optional parameter to test the free movement from a coordinate different fron the current one. @return False if no collision is detected, or the collisions points tuples """ position = self.position collide_rect = self.collide_rect if not source: source = position else: # I need a collide_rect traslated to the new source rx, ry = position[0]-int(source[0]), position[1]-int(source[1]) collide_rect.move_ip(rx, ry) v = Vector2.from_points(source, target) magnitude = v.get_magnitude() heading = v.normalize() distance = min(collide_rect.w, collide_rect.h) total_distance = 0 while True: total_distance+=distance movement = heading * total_distance x = movement.get_x() y = movement.get_y() collision = self.checkCollision(x, y, silent=True) if collision: return (x,y) if total_distance>=magnitude: return False
def normalizeDrawPositionBasedOn(self, reference, time_passed): """Slowly move drawn portion of the total level, for centering it on the given reference object. reference can be a GameSprite or a position tuple info. """ if type(reference)==tuple: referencePointOnScreen = reference else: referencePointOnScreen = reference.position if self.isPointAtScreenCenter(referencePointOnScreen, (160,160) ): return heading = Vector2.from_points(self.center, referencePointOnScreen) # More near to screen border, faster will be the screen centering procedure speed = self._centeringSpeed magnitude = heading.get_magnitude() if magnitude<120: pass elif magnitude<150: speed = speed*3 elif magnitude<200: speed = speed*5 elif magnitude>=250: speed = speed*6 heading.normalize() distance = time_passed * speed movement = heading * distance x = movement.get_x() y = movement.get_y() self.topleft = (self.topleft[0]+x,self.topleft[1]+y)
def hasFreeSightOn(self, sprite): """Check if the target sprite is in sight of the current character @sprite: the sprite to be tested. @return: True if the target sprite is in sight; False otherwise. """ # BBB: check if this can be enanched someway; the use of 5 pixel is ugly and imperfect to_target = Vector2.from_points(self.position, sprite.position) magnitude = to_target.get_magnitude() # 1 - False if sprite position is outside the character sight if self.sightRange<magnitude: return False # 2 - Now I need to get the line sight on the target to_target.normalize() magnitude_portion = 5 visual_obstacles = self.currentLevel['visual_obstacles'] screen_position = self.toScreenCoordinate() while magnitude>0: for obstacle in visual_obstacles: temp_v = (to_target*magnitude).as_tuple() temp_pos = screen_position[0]+temp_v[0], screen_position[1]+temp_v[1] if obstacle.collide_rect.collidepoint(temp_pos): logging.debug("%s can't see %s due to the presence of %s" % (self, sprite, obstacle)) return False magnitude-=magnitude_portion return True
def getHeadingTo(self, target): """Return the heading to a given object or position. Object must have a "position" attribute or be a position tuple itself. """ if hasattr(target, 'position'): position = target.position else: position = target heading = Vector2.from_points(self.position, position) return heading.normalize()
def update(self, time_passed): """Update method of pygame Sprite class. Overrided the one in Character main class because we need to handle user controls here. """ GameSprite.update(self, time_passed) if cblocals.global_lastMouseLeftClickPosition or cblocals.global_lastMouseRightClickPosition: self.stopThinking() if self._brain.active_state and self._brain.active_state.name!="controlled": return Character.update(self, time_passed) pressed_keys = pygame.key.get_pressed() # update stealth level if pressed_keys[K_LSHIFT] and self.canStealthAgain() and not self.stealth: self.stealth = True elif not pressed_keys[K_LSHIFT] and self.stealth: self.stealth = False # Check for mouse actions setted if cblocals.global_lastMouseLeftClickPosition: self.navPoint.set(self.currentLevel.transformToLevelCoordinate(cblocals.global_lastMouseLeftClickPosition)) cblocals.global_lastMouseLeftClickPosition = () if cblocals.global_lastMouseRightClickPosition and not self.isAttacking(): attackHeading = Vector2.from_points(self.position, self.currentLevel.transformToLevelCoordinate(cblocals.global_lastMouseRightClickPosition)) attackHeading.normalize() cblocals.global_lastMouseRightClickPosition = () # Right click on a distant enemy will move the hero towards him... if self.seeking: # enable the hero brain enemy = self.seeking print "Seeking %s" % enemy.name self.enemyTarget = enemy self._brain.setState("hunting") # ...or attack (even if moving)... else: self.setAttackState(attackHeading) if pygame.key.get_pressed()[K_z] and not self.stealth: self._brain.setState("retreat") if self.navPoint: self.moveBasedOnNavPoint(time_passed) if self._attackDirection: self.updateAttackState(time_passed)
def moveBasedOnNavPoint(self, time_passed, destination=None, relative=False): """Main method for move character using navPoint infos. If a destination is not specified, then the current character navPoint is used. @destination: can be None (navPoint will be taken), a point or a Vector2 instance @relative: destination is a relative offset from the character position """ # TODO: the current collision checking is broken: a too fast character can pass over an obstacle if not destination: destination = self.navPoint.get() if not destination: return else: if type(destination)==tuple and relative: ox, oy = destination cx, cy = self.position destination = (cx+ox, cy+oy) self.navPoint.set(destination) destination = self.navPoint.get() self.heading = Vector2.from_points(self.position, destination) magnitude = self.heading.get_magnitude() self.heading.normalize() distance = time_passed * self.speed # I don't wanna move over the destination! if distance>magnitude: distance = magnitude else: # I don't check for a new direction if I'm only fixing the last step distance direction = self._generateDirectionFromHeading(self.heading) self._checkDirectionChange(direction) movement = self.heading * distance self._updateStepTime(time_passed) x = movement.get_x() y = movement.get_y() collision = self.checkCollision(x, y) if not collision: self.move(x, y) if self.isNearTo(self.navPoint.as_tuple()): self.navPoint.next() else: #self.navPoint.reroute() self.navPoint.reset()
def adjustStealthIndexFromPosition(cls, rogueStealthIndex, rogue, prey): """Modify a passed stealthIndex of a rogue character checking also: 1) the distance between rogue and prey 2) the facing direction of the prey. The idea is that the rogue has good bonus if he's arriving from behind the prey. He gets no bonus if is arriving from beside the prey. Finally he get some malus if is arriving in front of the prey. Is also impossible that the prey didn't identify the hidden rogue if he's very near and directly in front of him! """ distance = rogue.v.get_distance_to(prey.v) preySight = int(prey.sightRange * prey.getPreySightRangeReductionFactor()) modifier = 1. if distance<preySight/5: modifier+=.2 # malus 20% elif distance<preySight/3: modifier+=.15 # malus 15% elif distance<preySight/3*2: modifier+=.10 # malus 10% # The rogue has malus if the prey is watching it: +25% (angle >=-30° and <=+30°) # The rogue has bonus if the prey is watching in the other direction: -20% (angle <=-150° and >=+150°) prey_to_rogue = Vector2.from_points(prey.position, rogue.position).normalize() angle_between = prey.heading.angle_between(prey_to_rogue) logging.debug("Prey heading: %s - Prey to rogue: %s - Angle: %s" % (prey.heading, prey_to_rogue, angle_between)) if angle_between>=-30 and angle_between<=30: modifier+=.25 elif angle_between<=-150 or angle_between>=150: modifier-=.2 rogueStealthIndex = rogueStealthIndex*modifier if rogueStealthIndex>.95: rogueStealthIndex=.95 elif rogueStealthIndex<.2: rogueStealthIndex=.2 return rogueStealthIndex
def moveBasedOnRetreatAction(self, time_passed): """See moveBasedOnRetreatAction of Character class. The playing character movement is based on the mouse position on the screen, but you can't retreat moving in front. """ cpos = self.toScreenCoordinate() mpos = pygame.mouse.get_pos() toMouse = Vector2.from_points(cpos,mpos) toMouse.normalize() rheading = -toMouse heading = self.heading angle_between = heading.angle_between(rheading) if angle_between>=-30 and angle_between<=30: return distance = time_passed * self.speed movement = rheading * distance x = movement.get_x() y = movement.get_y() if not self.checkCollision(x, y) and self.checkValidCoord(x, y): self.move(x, y)
def distanceFrom(self, sprite): """Return the distance between this sprite and another one""" return Vector2.from_points(self.position,sprite.position).get_magnitude()