示例#1
0
 def checkResyncAcknowledgement(self, msg):
     '''
     Checks whether the player position etc. matches the position encoded in
     the ResyncPlayerMsg or ResyncAcknowledgedMsg.
     '''
     return (isNear(self.pos[0], msg.xPos)
             and isNear(self.pos[1], msg.yPos)
             and isNear(self.yVel, msg.yVel)
             and isNear(self.angleFacing, msg.angle)
             and isNear(self.ghostThrust, msg.ghostThrust)
             and msg.health == max(self.health - self.zombieHits, 0))
示例#2
0
    def isRouteGoodEnough(self, route):
        if route.finishPoint is None:
            # Route isn't yet sure where it will end
            return True

        # Don't recalculate the route if we're still far from the target
        target = self.getTargetPos()
        error = distance(route.finishPoint, target)
        total = distance(self.bot.player.pos, target)
        if isNear(total, 0):
            return isNear(error, 0)
        if error / total >= 0.2:
            return False
        return True
    def getCollision(self, unit, vector, *, ignoreLedges=False):
        '''
        Returns a Colision object that results from moving the given unit by
        the given vector.
        '''
        if isNear(vector[0], 0) and isNear(vector[1], 0):
            return None

        collisions = []
        for polygon in self.getCandidatePolygons(unit, vector):
            if ignoreLedges and polygon.isLedge():
                continue
            collision = self.checkCollision(unit, vector, polygon)
            if collision:
                collisions.append(collision)
        if not collisions:
            return None
        return min(collisions, key=lambda c: c.travelDistance)
    def getMaxMotionToAxis(self, start, vector, x1, y1, x2, y2):
        '''
        :param start: the start position of a unit of this shape
        :param vector: the (deltaX, deltaY) that the unit is trying to move
        :param x1, y1: defines the first point of the axis to test
        :param x2, y2: defines the second point of the axis to test
        :return: (dist, contactPoint) where:
            dist: is the maximum distance the unit could travel in this
                direction and still lie entirely outside the axis defined by
                the given points.
            contactPoint: is the point of collision with the axis.

            If the unit is already inside the axis, returns (-0.05, None),
            so that collisions touching the surface are distinguishable from
            those where the unit is already inside the axis.
            If even an infinite motion would leave the unit outside the
            axis, returns (None,  None).

        Note that the parameter vector is only used to to determine the
        direction of motion, but its magnitude is ignored, so this method
        may return a distance greater than the length of vector.
        '''

        # Find the point on the ellipse which would collide
        nx, ny = (x2 - x1), (y2 - y1)
        x0, y0 = self.getSurfacePointFromTangent(start, (-nx, -ny))

        crossProduct1 = nx * (y1 - y0) - ny * (x1 - x0)
        ellipseTouchesAxis = isNear(crossProduct1, 0)
        if crossProduct1 < 0 and not ellipseTouchesAxis:
            # Point on ellipse is already inside the axis
            return (-0.05, None)

        mx, my = vector
        divisor = my * nx - mx * ny
        if divisor <= 0 or isNear(divisor, 0):
            # Moving away from or parallel to this axis
            return (None, None)

        x = ((y1 - y0) * mx * nx + x0 * my * nx - x1 * mx * ny) / divisor
        y = -((x1 - x0) * my * ny + y0 * mx * ny - y1 * my * nx) / divisor
        dist = ((x - x0) ** 2 + (y - y0) ** 2) ** 0.5

        return dist, (x, y)
    def update_from_player(self, player, show_phaseshift=True):
        # Consider horizontal movement of player.

        self.gun_angle = player.angleFacing
        self.bomber_time = (
            player.bomber.timeRemaining if player.bomber else None)
        self.emote = player.emote
        self.grabbed_surface_angle = player.grabbedSurfaceAngle
        self.grappling_hook_attached = player.grapplingHook.isAttached()

        if player.dead:
            self.action = PlayerAction.GHOST
        elif player.grabbedSurfaceAngle is not None:
            self.action = PlayerAction.GRABBING
        elif player.getGroundCollision():
            if isNear(player.xVel, 0):
                self.action = PlayerAction.STANDING
            else:
                x_motion = player.getXKeyMotion()
                if x_motion < 0:
                    walking = player.isFacingRight()
                elif x_motion > 0:
                    walking = not player.isFacingRight()
                else:
                    walking = False
                if walking:
                    self.action = PlayerAction.WALKING
                else:
                    self.action = PlayerAction.RUNNING
        elif player.yVel > 0:
            self.action = PlayerAction.FALLING
        else:
            self.action = PlayerAction.JUMPING

        self.has_shield = player.hasVisibleShield()
        self.has_shoxwave = bool(player.shoxwave)
        self.has_machine_gun = bool(player.machineGunner)
        self.has_ricochet = bool(player.hasRicochet)
        self.has_ninja = bool(player.ninja)
        self.has_disruption = bool(player.disruptive)
        self.has_elephant = player.hasElephant()
        self.flickering = (
            (player.phaseshift and show_phaseshift) or player.isInvulnerable())
        self.translucent = player.invisible

        if player.bot:
            self.head = HEAD_BOT
        else:
            self.head = player.head

        if player.team is None:
            self.team_colour = (255, 255, 255)
        else:
            self.team_colour = player.team.colour

        self.resyncing = player.resyncing
    def checkLedgeCollision(self, start, vector, polygon):
        '''
        Checks whether the a unit of this shape travelling along the given
        vector would collide with the given one-way open polygon.

        :param start: the start position of the unit
        :param vector: the (deltaX, deltaY) that the unit is moving
        :param polygon: the open polygon to test against
        :return: a Collision object, or None for no collision
        '''
        vectorDist = (vector[0] ** 2 + vector[1] ** 2) ** 0.5
        for x1, y1, x2, y2 in polygon.getEdges():
            dist, contactPoint = self.getMaxMotionToAxis(
                start, vector, x1, y1, x2, y2)

            if dist is None or dist >= vectorDist or dist < 0:
                # Does not collide with this infinite axis
                continue

            cx, cy = contactPoint
            if (
                    x1 <= cx <= x2 or x2 <= cx <= x1
                    or isNear(cx, x1) or isNear(cx, x2)):
                if (
                        y1 <= cy <= y2 or y2 <= cy <= y2
                        or isNear(cy, y1) or isNear(cy, y2)):
                    if isNear(dist, 0):
                        actualVector = (0.0, 0.0)
                    else:
                        f = dist / vectorDist
                        actualVector = (vector[0] * f, vector[1] * f)
                    surfaceAngle = atan2(y2 - y1, x2 - x1)
                    return Collision(
                        start, actualVector, contactPoint, surfaceAngle, ledge=True)

        points = list(polygon.getPoints())
        dist, vertex, surfaceAngle = self.getVertexCollision(
            start, vector, points, closed=False)

        if dist is None or dist >= vectorDist or dist <= 0:
            # Does not collide with any of the vertices
            return None

        if isNear(dist, 0):
            actualVector = (0.0, 0.0)
        else:
            f = dist / vectorDist
            actualVector = (vector[0] * f, vector[1] * f)
        return Collision(start, actualVector, vertex, surfaceAngle, ledge=True)
    def checkConcavePolygonCollision(self, start, vector, polygon):
        '''
        Checks whether the a unit of this shape travelling along the given
        vector would collide with the given fixed polygon.

        :param start: the start position of the unit
        :param vector: the (deltaX, deltaY) that the unit is moving
        :param polygon: the polygon to test against
        :return: a Collision object, or None for no collision
        '''

        # Check if entire polygon is outside of the motion path of this unit
        points = list(polygon.getPoints())
        for axis in self.getMotionBoundaries(start, vector):
            for point in points:
                if self.pointLiesInside(point, axis):
                    break
            else:
                # All points lie outside this boundary, so no collision occurs
                return None

        # Check an infinite line along each polygon edge to see how far the
        # unit could move before hitting that axis.
        vectorDist = (vector[0] ** 2 + vector[1] ** 2) ** 0.5
        edgeDist, edgeContact, edgeAngle = self.getEdgeCollision(
            start, vector, polygon)
        if edgeDist is None:
            # Entire motion path is separated by an edge axis,
            # so no collision occurs.
            return None

        if edgeDist < 0:
            # Start position lies partially or fully inside the polygon. Try
            # starting with a magical nudge.
            r = min(0.5, 0.1 / vectorDist)
            nudgeX = vector[0] * r
            nudgeY = vector[1] * r
            start2 = (start[0] + nudgeX, start[1] + nudgeY)
            vector2 = (vector[0] - nudgeX, vector[1] - nudgeY)

            edgeDist2, edgeContact2, edgeAngle2 = self.getEdgeCollision(
                start2, vector2, polygon)
            if edgeDist2 is None:
                # Entire motion path is separated by an edge axis,
                # so no collision occurs.
                return None
            if edgeDist2 >= 0:
                # No longer inside the polygon, so go with this calculation
                edgeDist = edgeDist2 + r * vectorDist
                edgeContact = edgeContact2
                edgeAngle = edgeAngle2
            else:
                # Still inside the polygon. Try nudging the other way.
                start2 = (start[0] - nudgeX, start[1] - nudgeY)

                edgeDist2, edgeContact2, edgeAngle2 = self.getEdgeCollision(
                    start2, vector2, polygon)
                if edgeDist2 is None:
                    return None

                if edgeDist2 >= 0:
                    edgeDist = edgeDist2 - r * vectorDist
                    edgeContact = edgeContact2
                    edgeAngle = edgeAngle2

        vertexDist, vertex, vertexAngle = self.getVertexCollision(
            start, vector, points)

        if vertexDist is None:
            # Tangent to ellipse does not separate shapes,
            # so edge collision occurs before vertex collision.
            finalDist = edgeDist
            if edgeContact is None:
                contactPoint = start
            else:
                contactPoint = edgeContact
            if edgeAngle is None:
                # Already inside polygon. Use normal to direction of travel.
                surfaceAngle = atan2(-vector[0], vector[1])
            else:
                surfaceAngle = edgeAngle
        elif vertexDist >= vectorDist:
            # Vertex collision would occur past end of intended motion path
            return None
        elif vertexDist < 0:
            # Unit has already passed the polygon
            return None
        else:
            # Vertex collision occurs before edge collision
            finalDist = vertexDist
            contactPoint = vertex
            surfaceAngle = vertexAngle

        if isNear(finalDist, 0):
            actualVector = (0.0, 0.0)
        else:
            f = finalDist / vectorDist
            actualVector = (vector[0] * f, vector[1] * f)

        return Collision(start, actualVector, contactPoint, surfaceAngle)
    def getVertexCollision(self, start, vector, points, closed=True):
        '''
        Calculates the greatest distance this shaped unit would move
        along the given path before colliding with a vertex of the given
        polygon.

        :param start: the starting point of the unit
        :param vector: the direction of motion
        :param points: the points of the polygon
        :param closed: True for a closed polygon, False for open
        :return: (dist, vertex, tangentAngle), where
            dist: the distance moved, or None if none of the vertices have a
                separating axis that aligns with the tangent of the ellipse
                (that is, none of the vertices can be hit without first hitting
                an edge).
            vertex: the point of collision
            tangentAngle: the angle of the tangent at the point of collision

        Note that dist may be negative, in cases where the ellipse has passed
        the polygon already.
        '''
        sx, sy = start

        # Transform into a coordinate system where the ellipse is a circle
        magnitude = (
            (vector[0] / self.rx) ** 2 + (vector[1] / self.ry) ** 2) ** 0.5
        mx = vector[0] / magnitude / self.rx
        my = vector[1] / magnitude / self.ry

        if closed:
            iterator = self.iterClosedPolygonPoints(points)
        else:
            iterator = self.iterOpenPolygonPoints(points)

        for lastPoint, point, nextPoint in iterator:
            x1 = (point[0] - sx) / self.rx
            y1 = (point[1] - sy) / self.ry

            # Project this vertex onto the circle
            radicand = 1 - (mx * y1 - my * x1) ** 2
            if radicand > 0 and not isNear(radicand, 0):
                # Entire circle will not miss this vertex
                k = mx * x1 + my * y1 - radicand ** 0.5
                if isNear(k, 0):
                    # Make sure that it's definitely not negative
                    k = 0.0
                elif k < 0:
                    # Unit is either inside or past this polygon
                    k2 = mx * x1 + my * y1 + radicand ** 0.5
                    if isNear(k2, 0):
                        # Make sure that it's definitely negative, for the
                        # sake of later tests that check if the collision is
                        # before or after the polygon.
                        k2 = -0.000001
                    if k2 > 0:
                        # Unit is inside this polygon
                        return None, None, None

                    # Unit is past this polygon, so use the other side of
                    # the circle for subsequent calculations.
                    k = k2

                # Find collision point on circle
                cx = x1 - k * mx
                cy = y1 - k * my

                # Check the slope of the tangent is within allowed bounds
                tx = -cy * self.rx
                ty = cx * self.ry
                prevSegmentProduct = tx * (point[1] - lastPoint[1]) \
                                     - ty * (point[0] - lastPoint[0])
                inPrevSegment = prevSegmentProduct > 0 and not isNear(
                    prevSegmentProduct, 0)
                nextSegmentProduct = (nextPoint[0] - point[0]) * ty \
                                     - (nextPoint[1] - point[1]) * tx
                inNextSegment = nextSegmentProduct > 0 and not isNear(
                    nextSegmentProduct, 0)
                if inPrevSegment and inNextSegment:
                    # Because both objects are convex, only one collision is
                    # possible, so no need to check other vertices.
                    dist = k * ((mx * self.rx) ** 2 + (my * self.ry) ** 2) ** 0.5
                    return dist, point, atan2(-ty, -tx)

        return None, None, None
 def pointLiesInside(self, point, axis):
     # NOTE: boundary is counted as outside
     origin, rotatedAxes = axis
     s, t = rotatedAxes.rotatedFromGlobal(point, origin=origin)
     return t > 0 and not isNear(t, 0, epsilon=0.01)