Beispiel #1
0
    def get_output(self, packet: GameTickPacket) -> SimpleControllerState:
        self.quick_chat_handler.handle_quick_chats(packet)

        # Collect data from the packet
        self.time = packet.game_info.seconds_elapsed
        ball_location = Vector2(packet.game_ball.physics.location.x,
                                packet.game_ball.physics.location.y)
        my_car = packet.game_cars[self.index]
        self.car = my_car
        car_location = Vector2(my_car.physics.location.x,
                               my_car.physics.location.y)
        car_velocity = Vector3(my_car.physics.velocity.x,
                               my_car.physics.velocity.y,
                               my_car.physics.velocity.z)
        car_direction = get_car_facing_vector(my_car)
        car_to_ball = ball_location - car_location
        team_sign = (1 if my_car.team == 0 else -1)
        enemy_goal = Vector2(0, team_sign * 5120)
        kickoff = (ball_location.x == 0 and ball_location.y == 0)
        impact, impact_time = get_impact(
            self.get_ball_prediction_struct(), self.car,
            Vector3(packet.game_ball.physics.location.x,
                    packet.game_ball.physics.location.y,
                    packet.game_ball.physics.location.z), self.renderer)
        rotation_matrix = Matrix3D([
            my_car.physics.rotation.pitch, my_car.physics.rotation.yaw,
            my_car.physics.rotation.roll
        ])
        # Hi robbie!
        '''
        # don't crash if winning by too much
        game_score = QuickChatHandler.get_game_score(packet)
        if game_score[my_car.team] - game_score[1 - my_car.team] >= 4:
            return self.controller
        '''

        # Handle bouncing
        ball_bounces: List[Slice] = get_ball_bounces(
            self.get_ball_prediction_struct())
        bounce_location = None
        for b in ball_bounces:
            time: float = b.game_seconds - self.time
            if time < impact_time - 0.5:
                continue
            bounce_location: Vector2 = Vector2(b.physics.location)
            break
        if bounce_location is None:
            time = 0

        # Handle aerials
        if self.aerial is not None:
            if self.car.has_wheel_contact and self.car.jumped:
                # Give up on an aerial
                self.aerial = None
            else:
                # Get the output of the aerial
                return self.aerial.execute()
        elif self.aerial is None and time > 2.5 and impact.z > 500 and car_velocity.length < 1000 and team_sign * car_location.y < team_sign * ball_location.y:
            # Start a new aerial
            self.aerial = Aerial(self.time)
            return self.aerial.execute(packet, self.index)

        # Set a destination for Anarchy to reach
        impact_projection = project_to_wall(car_location,
                                            impact.flatten() - car_location)
        avoid_own_goal = impact_projection.y * team_sign < -5000
        wait = (packet.game_ball.physics.location.z > 200
                and my_car.physics.location.z < 200)
        if wait:
            destination = bounce_location
        else:
            destination = impact.flatten()
        if kickoff:
            pass
        elif avoid_own_goal:
            offset = (impact_time * 200 + 100)
            destination += Vector2(offset * -sign(impact_projection.x),
                                   140 if wait else 0)
        elif abs(
                ball_location.x
        ) < 750 or team_sign * car_location.y > team_sign * ball_location.y or (
                abs(ball_location.x) > 3200
                and abs(ball_location.x) + 100 > abs(car_location.x)):
            destination.y -= max(
                abs(car_to_ball.y) / 2.9, 70 if wait else 110) * team_sign
        else:
            destination += (destination - enemy_goal).normalized * max(
                car_to_ball.length / 3.4, 60 if wait else 100)
        if abs(car_location.y > 5120):
            destination.x = min(700,
                                max(-700,
                                    destination.x))  #Don't get stuck in goal
        car_to_destination = (destination - car_location)

        # Rendering
        self.renderer.begin_rendering()
        # commented out due to performance concerns
        # self.renderer.draw_polyline_3d([[car_location.x+triforce(-20,20), car_location.y+triforce(-20,20), triforce(shreck(200),200)] for i in range(40)], self.renderer.cyan())
        self.renderer.draw_rect_2d(
            0, 0, 3840, 2160, True, self.renderer.create_color(
                64, 246, 74, 138))  # first bot that supports 4k resolution!
        self.renderer.draw_string_2d(triforce(20, 50), triforce(10, 20), 5, 5,
                                     'ALICE NAKIRI IS BEST GIRL',
                                     self.renderer.white())
        self.renderer.draw_string_2d(triforce(20, 50), triforce(90, 100), 2, 2,
                                     '(zero two is a close second)',
                                     self.renderer.lime())
        self.renderer.draw_string_2d(
            20, 100, 2, 2,
            "Max Speed: " + str(int(estimate_max_speed(self.car))),
            self.renderer.white())
        self.renderer.draw_line_3d([destination.x, destination.y, impact.z],
                                   [impact.x, impact.y, impact.z],
                                   self.renderer.blue())
        if avoid_own_goal:
            self.renderer.draw_line_3d(
                [car_location.x, car_location.y, 0],
                [impact_projection.x, impact_projection.y, 0],
                self.renderer.yellow())
        self.renderer.end_rendering()

        self.zero_two.render(self.renderer)

        # Choose whether to drive backwards or not
        wall_touch = (distance_from_wall(impact.flatten()) < 250
                      and team_sign * impact.y < 4000)
        local = rotation_matrix.dot(
            Vector3(car_to_destination.x, car_to_destination.y,
                    (impact.z if wall_touch else 17.010000228881836) -
                    my_car.physics.location.z))
        steer_correction_radians = math.atan2(local.y, local.x)
        backwards = (math.cos(steer_correction_radians) < 0)
        if backwards:
            if steer_correction_radians != 0:
                steer_correction_radians = -(
                    steer_correction_radians -
                    sign(steer_correction_radians) * math.pi)
            else:
                steer_correction_radians = math.pi

        # Speed control
        target_velocity = (((bounce_location - car_location).length /
                            time) if time > 0 else 2300)
        velocity_change = (target_velocity - car_velocity.flatten().length)
        if velocity_change > 200 or target_velocity > 1410:
            self.controller.boost = (abs(steer_correction_radians) < 0.2
                                     and not my_car.is_super_sonic
                                     and not backwards)
            self.controller.throttle = (1 if not backwards else -1)
        elif velocity_change > -50:
            self.controller.boost = False
            self.controller.throttle = 0
        else:
            self.controller.boost = False
            self.controller.throttle = (-1 if not backwards else 1)

        # Steering
        turn = clamp11(steer_correction_radians * 2)
        self.controller.steer = turn
        self.controller.handbrake = (abs(turn) > 1
                                     and not my_car.is_super_sonic)

        # Dodging
        self.controller.jump = False
        dodge_for_speed = (velocity_change > 700 and not backwards
                           and my_car.boost < 10
                           and car_to_destination.size > 1000
                           and abs(steer_correction_radians) < 0.1)
        if (((car_to_ball.size < 300 and
              packet.game_ball.physics.location.z < 300) or dodge_for_speed)
                and car_velocity.size > 1200) or self.dodging:
            dodge(
                self,
                car_direction.correction_to(
                    car_to_destination if impact_time > 0.8 else car_to_ball),
                ball_location)

        # Half-flips
        if backwards and impact_time > 0.6 and car_velocity.size > 900 and abs(
                steer_correction_radians) < 0.1 or self.halfflipping:
            halfflip(self)

        if not self.car.has_wheel_contact and not (
                self.dodging or self.halfflipping):  # Recovery
            self.controller.roll = clamp11(self.car.physics.rotation.roll *
                                           -0.7)
            self.controller.pitch = clamp11(self.car.physics.rotation.pitch *
                                            -0.7)
            self.controller.boost = False

        return self.controller
Beispiel #2
0
class Anarchy(BaseAgent):
    def __init__(self, name, team, index):
        super().__init__(name, team, index)
        self.controller = SimpleControllerState()
        self.dodging = False
        self.halfflipping = False
        self.dodge_angle = 0
        self.time = 0
        self.next_dodge_time = 0
        self.quick_chat_handler: QuickChatHandler = QuickChatHandler(self)
        self.zero_two: Optional[ColoredWireframe] = None
        self.aerial: Optional[Aerial] = None
        self.steer_correction_radians: float = 0
        self.demo: Optional[Demolition] = None
        self.gamemode: Gamemode = Gamemode.SOCCAR
        self.jukebox: Jukebox = Jukebox(self)
        self.action: ActionBase = None
        self.rotation_matrix: Matrix3D = None
        self.rotation_velocity: Vector3 = None
        self.car_direction: Vector3 = None
        self.impact: Vector3 = None

    def load_config(self, config_header):
        render_statue = config_header.getboolean("render_statue")
        if render_statue:
            self.zero_two = unzip_and_build_zero_two()

        self.render_disco_ball = config_header.getboolean("render_disco_ball")
        self.enable_dodge_bounce_audio = config_header.getboolean(
            "enable_dodge_bounce_audio")
        self.enable_goal_music = config_header.getboolean("enable_goal_music")
        self.enable_ball_touch_audio = config_header.getboolean(
            "enable_ball_touch_audio")

    @staticmethod
    def create_agent_configurations(config: ConfigObject):
        params = config.get_header(BOT_CONFIG_AGENT_HEADER)
        params.add_value("render_statue", bool, default=False)
        params.add_value("render_disco_ball", bool, default=False)
        params.add_value("enable_dodge_bounce_audio", bool, default=False)
        params.add_value("enable_goal_music", bool, default=False)
        params.add_value("enable_ball_touch_audio", bool, default=False)

    def get_output(self, packet: GameTickPacket) -> SimpleControllerState:
        self.quick_chat_handler.handle_quick_chats(packet)
        if self.enable_goal_music:
            self.jukebox.update(packet)

        # Collect data from the packet
        self.mode = (Gamemode.DROPSHOT if self.get_field_info().num_boosts == 0
                     else Gamemode.SOCCAR)
        self.time = packet.game_info.seconds_elapsed
        ball_location = Vector3(packet.game_ball.physics.location)
        self.car = packet.game_cars[self.index]
        car_location = Vector3(self.car.physics.location)
        car_velocity = Vector3(self.car.physics.velocity)
        car_direction = get_car_facing_vector(self.car)
        self.car_direction = car_direction
        car_to_ball = ball_location - car_location
        is_1v1 = (packet.num_cars == 2
                  and packet.game_cars[not self.index].team != self.car.team)
        team_sign = (1 if self.car.team == (
            is_1v1 and packet.teams[self.car.team].score == 7
            and packet.teams[not self.car.team].score == 0) else -1)
        enemy_goal = Vector2(0, team_sign * 5120)
        kickoff = packet.game_info.is_kickoff_pause
        ball_prediction = self.get_ball_prediction_struct()
        impacts = [
            get_impact(ball_prediction, car, self.time, self.renderer)
            for car in packet.game_cars[:packet.num_cars]
        ]
        impact, impact_time = impacts[self.index]
        self.impact = impact
        impact_projection = project_to_wall(car_location,
                                            impact.flatten() - car_location)
        rotation_matrix = Matrix3D([
            self.car.physics.rotation.pitch,
            self.car.physics.rotation.yaw,
            self.car.physics.rotation.roll,
        ])
        self.rotation_matrix = rotation_matrix
        rotation_velocity = rotation_matrix.dot(
            self.car.physics.angular_velocity)
        self.rotation_velocity = rotation_velocity
        car_local_velocity = rotation_matrix.dot(car_velocity)
        correct_side_of_ball: bool = (
            (impact_projection.y - car_location.y) * team_sign > 0)
        # Hi robbie!
        if self.time == packet.game_ball.latest_touch.time_seconds and self.enable_ball_touch_audio:
            print(
                "\a"
            )  # for all the people who mute anarchy because they dont like 'boing' :)

        if self.render_disco_ball:
            # this kinda kills the anime and idk what to do about it
            self.renderer.begin_rendering("disco")
            for i in range(100):
                hmmm = ball_location + Vector3(
                    random.randint(-1000, 1000),
                    random.randint(-1000, 1000),
                    random.randint(-1000, 1000),
                )
                self.renderer.draw_line_3d(
                    [ball_location.x, ball_location.y, ball_location.z],
                    [hmmm.x, hmmm.y, hmmm.z],
                    self.renderer.create_color(
                        255,
                        random.randint(0, 255),
                        random.randint(0, 255),
                        random.randint(0, 255),
                    ),
                )
            self.renderer.end_rendering()

        # Action.
        if self.action:
            print(self.action.__class__.__name__)
            if not hasattr(self.action,
                           "finished") or not self.action.finished:
                return self.action.step(packet)
            self.action = None

        # Handle bouncing
        ball_bounces: List[Slice] = get_ball_bounces(
            self.get_ball_prediction_struct())
        bounce_location = None
        for b in ball_bounces:
            time: float = b.game_seconds - self.time
            if time < impact_time - 0.3:
                continue
            bounce_location: Vector2 = Vector2(b.physics.location)
            break
        if bounce_location is None:
            time = 0

        # Handle aerials
        if self.aerial is not None:
            if self.car.has_wheel_contact and self.aerial.jt + 0.5 < self.time:
                # Give up on an aerial
                self.aerial = None
            else:
                # Get the output of the aerial
                aerial_output = self.aerial.execute(
                    packet, self.index, self.get_ball_prediction_struct())
                if self.aerial.target is not None:
                    self.renderer.begin_rendering()
                    self.renderer.draw_line_3d(
                        car_location,
                        car_location + self.aerial.target,
                        self.renderer.white(),
                    )
                    self.renderer.end_rendering()
                return aerial_output
        if (self.aerial is None and self.car.has_wheel_contact
                and impact.z - self.car.physics.location.z > 500
                and car_velocity.length > 100 and self.demo is None
                and (self.car.boost > 20 or
                     (abs(time - 0.6) < 0.1 and
                      (impact - car_location).flatten().length < 1000))
                and abs(self.steer_correction_radians) < 0.4
                and impact_time < 2.5
                and (impact_projection.y * team_sign > -5000
                     or abs(impact_projection.x) > 1000) and
                math.cos(car_velocity.angle_between(impact - car_location)) *
                car_velocity.flatten().length > 400):
            # Start a new aerial
            self.aerial = Aerial(self.time)
            return self.aerial.execute(packet, self.index,
                                       self.get_ball_prediction_struct())

        # Set a destination for Anarchy to reach
        teammate_going_for_ball: bool = False
        for index, car in enumerate(packet.game_cars[:packet.num_cars]):
            if car.team == self.team and index != self.index:
                teammate_location = Vector3(car.physics.location)
                teammate_correct_side_of_ball: bool = (
                    (impacts[index][0].y - teammate_location.y) * team_sign >
                    0)
                if correct_side_of_ball and not teammate_correct_side_of_ball:
                    continue
                if impacts[index][1] + 0.1 < impact_time or (
                        not correct_side_of_ball
                        and teammate_correct_side_of_ball):
                    teammate_going_for_ball = True
                    self.renderer.begin_rendering("teammate")
                    self.renderer.draw_line_3d(car_location, teammate_location,
                                               self.renderer.black())
                    self.renderer.draw_rect_3d(teammate_location, 4, 4, True,
                                               self.renderer.black())
                    self.renderer.end_rendering()
                    break

        avoid_own_goal = impact_projection.y * team_sign < -5000
        wait = ball_location.z > 200 and self.car.physics.location.z < 200
        take_serious_shot = False
        obey_turning_radius = True  # Slow down if the target is in the turning radius
        demoing = self.demo is not None
        close_boost = closest_boost(
            Vector3(-impact.x, impact.y, 0) if teammate_going_for_ball
            and impact_time < 2.5 and not kickoff else car_location,
            self.get_field_info().boost_pads,
            packet.game_boosts,
        )
        convenient_boost = (close_boost
                            and (close_boost - car_location).flatten().length *
                            (5 if is_1v1 else 3) <
                            (impact - car_location).flatten().length)
        need_boost = not self.car.is_super_sonic and self.car.boost < (
            40 if convenient_boost else 30)
        park_car = False
        not_our_kickoff = kickoff and teammate_going_for_ball

        if wait and bounce_location is not None:
            destination = Vector3(bounce_location.x, bounce_location.y, 0)
        else:
            destination = impact
            time = 0
        if kickoff and not not_our_kickoff:
            destination = ball_location + Vector3(0, -92.75 * team_sign, 0)
            obey_turning_radius = False
        elif (avoid_own_goal and not kickoff and not demoing and
              (not teammate_going_for_ball or impact.y * team_sign > -4000)):
            offset = max(0, impact_time - 0.5) * 160 + 110
            destination += Vector2(offset * -sign(impact_projection.x),
                                   (140 if wait else 0) * -team_sign)
        elif (not_our_kickoff or teammate_going_for_ball or demoing
              or (close_boost and need_boost and
                  (not is_1v1 or convenient_boost))):
            if close_boost:
                destination = close_boost

            demo_location = None
            if (demoing or not need_boost) and not kickoff:
                if not demoing:
                    self.demo = Demolition.start_demo(self, packet)
                demo_location, demo_time = (self.demo.get_destination(packet)
                                            if self.demo is not None else
                                            (None, 0))
                if demo_location is not None:
                    destination = demo_location
                    obey_turning_radius = False
                    demoing = True
                    time = 0
            if demo_location is None:
                self.demo = None
                demoing = False
                if ((not need_boost and car_to_ball.length > 2500)
                        if not not_our_kickoff else
                    (abs(car_location.x) < 50)):  # Middle
                    park_car = True
                    destination = Vector2(0, -4900 * team_sign)
                    destination += (impact.flatten() - destination) / 8
                    destination = Vector3(destination.x, destination.y, 17)
        elif team_sign * (car_location.y - ball_location.y) > 0:
            destination.x += max(car_to_ball.length / 3, 80
                                 if wait else 120) * -sign(impact_projection.x)
        else:
            destination += (destination - enemy_goal).normalized * max(
                (destination - car_location).length /
                ((2.1 + impact_time / 1.9) if take_serious_shot else 3.7),
                60 if wait else 110,
            )
            take_serious_shot = True
        if abs(car_location.y) > 5120:
            destination.x = min(700,
                                max(-700,
                                    destination.x))  # Don't get stuck in goal
        car_to_destination = destination - car_location

        # Rendering
        self.renderer.begin_rendering()
        if kickoff:
            self.renderer.draw_string_2d(20, 140, 2, 2, "Kickoff",
                                         self.renderer.white())
        if take_serious_shot:
            self.renderer.draw_string_2d(20, 140, 2, 2, "Shoot",
                                         self.renderer.white())
        self.renderer.draw_line_3d(
            [destination.x, destination.y, impact.z],
            [impact.x, impact.y, impact.z],
            self.renderer.blue(),
        )
        if avoid_own_goal:
            self.renderer.draw_line_3d(
                [car_location.x, car_location.y, 0],
                [impact_projection.x, impact_projection.y, 0],
                self.renderer.yellow(),
            )
        if not demoing:
            self.renderer.clear_screen(Demolition.get_render_name(self))
        if park_car:
            self.renderer.draw_string_2d(20, 140, 2, 2, "Parking!",
                                         self.renderer.yellow())
        self.renderer.end_rendering()

        if self.zero_two is not None and self.time > 10.0:
            self.zero_two.render(self.renderer)

        # Choose whether to drive backwards or not
        wall_touch = (distance_from_wall(impact.flatten()) < 500
                      and team_sign * impact.y < 4000)
        local = rotation_matrix.dot(
            Vector3(
                car_to_destination.x,
                car_to_destination.y,
                (impact.z if wall_touch else 17.010000228881836) -
                self.car.physics.location.z,
            ))
        self.steer_correction_radians = math.atan2(local.y, local.x)
        slow_down = (abs(self.steer_correction_radians) > 0.2
                     and inside_turning_radius(local, car_velocity.length)
                     and obey_turning_radius)
        if slow_down:
            self.renderer.begin_rendering()
            self.renderer.draw_string_2d(
                triforce(20, 50),
                triforce(10, 20),
                6,
                6,
                "Uh Oh",
                self.renderer.pink() if
                (self.time % 0.5) < 0.25 else self.renderer.red(),
            )
            self.renderer.end_rendering()
        turning_radians = self.steer_correction_radians
        backwards = (math.cos(turning_radians) < 0 and not demoing
                     and not self.car.is_super_sonic)
        if backwards:
            turning_radians = invert_angle(turning_radians)
        throttle_sign = -1 if backwards else 1

        # Speed control
        target_velocity = (((bounce_location - car_location).length /
                            time) if time > 0 else 2300)
        velocity_change = target_velocity - car_velocity.flatten().length
        if park_car:
            self.controller.boost = False
            self.controller.throttle = (clamp11(
                (destination - car_location).length / 2000) * throttle_sign)
        elif slow_down:
            if car_velocity.length > 400:
                self.controller.throttle = -sign(
                    rotation_matrix.data[0].dot(destination - car_location))
                self.controller.boost = self.controller.throttle > 0
            else:
                self.controller.throttle = 0
        elif velocity_change > 300 or target_velocity > 1410 or demoing:
            self.controller.boost = (abs(turning_radians) < 0.2
                                     and not self.car.is_super_sonic
                                     and not backwards)
            if self.controller.boost and self.mode is Gamemode.DROPSHOT:
                self.controller.boost = 30 < self.car.boost - (
                    2200 - car_velocity.length) / (
                        (911 + (2 / 3))
                        if self.car.has_wheel_contact else 1000) * (100 / 3)
            self.controller.throttle = throttle_sign
        elif velocity_change > -150:
            self.controller.boost = False
            self.controller.throttle = 0
        else:
            self.controller.boost = False
            self.controller.throttle = -throttle_sign

        # Steering
        turn = clamp11(turning_radians * 4)
        self.controller.steer = turn
        self.controller.handbrake = (abs(turning_radians) > 1.1
                                     and not self.car.is_super_sonic
                                     and not (slow_down or park_car))

        # prevent inefficient powerslides
        if sign(rotation_velocity.z) != sign(self.controller.steer) or sign(
                car_local_velocity.x) != sign(self.controller.throttle):
            self.controller.handbrake = False

        # Dodging
        self.controller.jump = False
        dodge_for_speed = (velocity_change > 500 and not backwards
                           and self.car.boost < 14
                           and self.time > self.next_dodge_time + 0.85
                           and car_to_destination.size >
                           (2000 if take_serious_shot else 1200)
                           and abs(turning_radians) < 0.1 and not demoing)
        if kickoff:
            dodge_for_speed = car_velocity.size > 1250
        if (((car_to_ball.flatten().size < 400 and ball_location.z < 300)
             or dodge_for_speed) and car_velocity.size > 1100) or self.dodging:
            dodge_at_ball = impact_time < 0.4
            dodge_direction = car_to_ball if dodge_at_ball else car_to_destination
            self.action = Dodge(self, dodge_direction)
            return self.action.step(packet)

        # Half-flips
        if (backwards and not park_car and
            (time if wait else impact_time) > 0.6 and car_velocity.size > 900
                and abs(turning_radians) < 0.1 or self.halfflipping):
            halfflip(self, rotation_velocity)

        # Recovery
        if not (self.dodging
                or self.halfflipping) and not self.car.has_wheel_contact:
            self.action = Recover(self,
                                  rotation_velocity,
                                  allow_yaw_wrap=(car_location.z > 250))
            return self.action.step(packet)

        return self.controller

    def handleBribbleSynergy(self, packet: GameTickPacket):
        # To be fair, you have to have a very high IQ to understand this function. The logic is extremely subtle,
        # and without a solid grasp of rocket league physics most of the mechanics will go over a typical coders head.
        # There's also Anarchy's nihilistic outlook, which is deftly woven into his characterisation- his personal
        # philosophy draws heavily from ATBA literature, for instance. The fans understand this stuff; they have the
        # intellectual capacity to truly appreciate the depths of these mechanics, to realise that they're not just
        # solid- they say something deep about RLBOT. As a consequence people who dislike this function ARE idiots-
        # of course they wouldn't appreciate, for instance, the mechanics in Anarchy's existential catchphrase "boiing.mp4"
        # which itself is a cryptic reference to tareharts epic Fathers and Sons. I'm smirking right now just imagining
        # one of those addlepated simpletons scratching their heads in confusion as L0laapk3's genius wit unfolds itself
        # in this function. What fools.. How I pity them. 😂

        # And yes, by the way, i DO have an Anarchy tattoo. And no, you cannot see it. It's for the ladies' eyes only-
        # and even then they have to demonstrate that they're within 5 IQ points of my own (preferably lower)
        # beforehard. Nothin personnel botmaker 😎
        pass
Beispiel #3
0
    def get_output(self, packet: GameTickPacket) -> SimpleControllerState:
        self.quick_chat_handler.handle_quick_chats(packet)

        # Collect data from the packet
        self.mode = (Gamemode.DROPSHOT if self.get_field_info().num_boosts == 0
                     else Gamemode.SOCCAR)
        self.time = packet.game_info.seconds_elapsed
        ball_location = Vector3(packet.game_ball.physics.location)
        self.car = packet.game_cars[self.index]
        car_location = Vector3(self.car.physics.location)
        car_velocity = Vector3(self.car.physics.velocity)
        car_direction = get_car_facing_vector(self.car)
        car_to_ball = ball_location - car_location
        is_1v1 = (packet.num_cars == 2
                  and packet.game_cars[not self.index].team != self.car.team)
        team_sign = (1 if self.car.team == (
            is_1v1 and packet.teams[self.car.team].score == 7
            and packet.teams[not self.car.team].score == 0) else -1)
        enemy_goal = Vector2(0, team_sign * 5120)
        kickoff = (ball_location.x == 0 and ball_location.y == 0)
        impact, impact_time = get_impact(
            self.get_ball_prediction_struct(), self.car,
            Vector3(packet.game_ball.physics.location.x,
                    packet.game_ball.physics.location.y,
                    packet.game_ball.physics.location.z), self.renderer)
        impact_projection = project_to_wall(car_location,
                                            impact.flatten() - car_location)
        rotation_matrix = Matrix3D([
            self.car.physics.rotation.pitch, self.car.physics.rotation.yaw,
            self.car.physics.rotation.roll
        ])
        rotation_velocity = rotation_matrix.dot(
            self.car.physics.angular_velocity)
        correct_side_of_ball: bool = (
            (impact_projection.y - car_location.y) * team_sign > 0)
        # Hi robbie!

        # Handle bouncing
        ball_bounces: List[Slice] = get_ball_bounces(
            self.get_ball_prediction_struct())
        bounce_location = None
        for b in ball_bounces:
            time: float = b.game_seconds - self.time
            if time < impact_time - 0.3:
                continue
            bounce_location: Vector2 = Vector2(b.physics.location)
            break
        if bounce_location is None:
            time = 0

        # Handle aerials
        if self.aerial is not None:
            if self.car.has_wheel_contact and self.aerial.jt + 0.5 < self.time:
                # Give up on an aerial
                self.aerial = None
            else:
                # Get the output of the aerial
                aerial_output = self.aerial.execute(
                    packet, self.index, self.get_ball_prediction_struct())
                if self.aerial.target is not None:
                    self.renderer.begin_rendering()
                    self.renderer.draw_line_3d(
                        car_location, car_location + self.aerial.target,
                        self.renderer.white())
                    self.renderer.end_rendering()
                return aerial_output
        if self.aerial is None and self.car.has_wheel_contact and impact.z - self.car.physics.location.z > 500 and car_velocity.length > 100 and self.demo is None and\
           (self.car.boost > 20 or (abs(time - 0.6) < 0.1 and (impact - car_location).flatten().length < 1000))\
            and abs(self.steer_correction_radians) < 0.4 and impact_time < 2.5 and (impact_projection.y * team_sign > -5000 or abs(impact_projection.x) > 1000)\
            and math.cos(car_velocity.angle_between(impact - car_location)) * car_velocity.flatten().length > 400:
            # Start a new aerial
            self.aerial = Aerial(self.time)
            return self.aerial.execute(packet, self.index,
                                       self.get_ball_prediction_struct())

        # Set a destination for Anarchy to reach
        teammate_going_for_ball: bool = False
        min_teammate_distance = float("inf")
        for index, car in enumerate(packet.game_cars[:packet.num_cars]):
            if self.car.team == self.team and index != self.index:
                teammate_to_ball: Vector3 = ball_location - Vector3(
                    self.car.physics.location)
                vector = get_car_facing_vector(self.car)
                teammate_facing_direction: Vector3 = Vector3(
                    vector.x, vector.y, 0)
                teammate_velocity_direction: Vector3 = Vector3(
                    self.car.physics.velocity)
                min_teammate_distance = min(min_teammate_distance,
                                            teammate_to_ball.length)
                if teammate_to_ball.length < (car_to_ball.length if correct_side_of_ball else max(1.8 * car_to_ball.length, 900)) and teammate_to_ball.y * team_sign > 0 and \
                        (abs(teammate_to_ball.angle_between(teammate_facing_direction)) < 0.4 * math.pi or
                         abs(teammate_to_ball.angle_between(teammate_velocity_direction)) < 0.4 * math.pi or
                         teammate_to_ball.length < 1200):
                    teammate_going_for_ball = True
                    self.renderer.begin_rendering('teammate')
                    self.renderer.draw_line_3d(car_location,
                                               self.car.physics.location,
                                               self.renderer.black())
                    self.renderer.draw_rect_3d(self.car.physics.location, 4, 4,
                                               True, self.renderer.black())
                    self.renderer.end_rendering()
                    break

        avoid_own_goal = impact_projection.y * team_sign < -5000
        wait = (ball_location.z > 200 and self.car.physics.location.z < 200)
        #take_serious_shot = (not kickoff and correct_side_of_ball and ball_velocity.flatten().length < 3000 and abs((impact - car_location).flatten().normalized.y) > 0.75)
        take_serious_shot = (
            not kickoff and correct_side_of_ball
            and impact.y * team_sign > 1000 + min(2, impact_time) * 500)
        obey_turning_radius = True  # Slow down if the target is in the turning radius
        demoing = (self.demo is not None)
        need_boost = (not self.car.is_super_sonic and self.car.boost < 30)
        close_boost = closest_boost(Vector3(-impact.x, impact.y, 0) if teammate_going_for_ball and impact_time < 2.5 and not kickoff else car_location\
                                    , self.get_field_info().boost_pads, packet.game_boosts)
        park_car = False
        not_our_kickoff = (kickoff and
                           min_teammate_distance < car_to_ball.length - 100)

        if wait and bounce_location is not None:
            destination = Vector3(bounce_location.x, bounce_location.y, 0)
        else:
            destination = impact
            time = 0
        if kickoff and not not_our_kickoff:
            destination = ball_location + Vector3(0, -92.75 * team_sign, 0)
            obey_turning_radius = False
        elif avoid_own_goal and not not_our_kickoff and not demoing and (
                not teammate_going_for_ball or impact.y * team_sign > -4000):
            offset = (max(0, impact_time - 0.5) * 160 + 110)
            destination += Vector2(offset * -sign(impact_projection.x),
                                   (140 if wait else 0) * -team_sign)
            obey_turning_radius = True
        elif not_our_kickoff or teammate_going_for_ball or self.demo is not None or \
             (close_boost and need_boost and (close_boost - car_location.flatten()).length * 5 < car_to_ball.length):
            if close_boost: destination = close_boost
            obey_turning_radius = True

            demo_location = None
            if not need_boost and not not_our_kickoff:
                if self.demo is None:
                    self.demo = Demolition.start_demo(self, packet)
                demo_location, demo_time = (self.demo.get_destination(packet)
                                            if self.demo is not None else None)
                if demo_location is not None and (demoing or
                                                  demo_time < max_demo_time):
                    destination = demo_location
                    obey_turning_radius = False
                    demoing = True
                    time = 0
            if not demoing or self.demo is None or demo_location is None:
                self.demo = None
                demoing = False
                if (not need_boost and car_to_ball.length > 2500
                    ) if not not_our_kickoff else (
                        abs(car_location.x) < 50):  #Middle
                    park_car = True
                    destination = Vector2(0, -4900 * team_sign)
                    destination += (impact.flatten() - destination) / 8
                    destination = Vector3(destination.x, destination.y, 17)
        elif abs(ball_location.x) < 750 or (
                not take_serious_shot and
            (team_sign * car_location.y > team_sign * ball_location.y or
             (abs(ball_location.x) > 3200
              and abs(ball_location.x) + 100 > abs(car_location.x)))):
            destination.y -= max(
                abs(car_to_ball.y) / 3.3, 70 if wait else 100) * team_sign
        else:
            destination += (destination - enemy_goal).normalized * max(
                (destination - car_location).length /
                ((2.1 + impact_time / 1.9) if take_serious_shot else 3.7),
                60 if wait else 110)
        if abs(car_location.y) > 5120:
            destination.x = min(700,
                                max(-700,
                                    destination.x))  #Don't get stuck in goal
        car_to_destination = (destination - car_location)

        # Rendering
        self.renderer.begin_rendering()
        if kickoff:
            self.renderer.draw_string_2d(20, 140, 2, 2, "Kickoff",
                                         self.renderer.white())
        if take_serious_shot:
            self.renderer.draw_string_2d(20, 140, 2, 2, "Shoot",
                                         self.renderer.white())
        self.renderer.draw_line_3d([destination.x, destination.y, impact.z],
                                   [impact.x, impact.y, impact.z],
                                   self.renderer.blue())
        if avoid_own_goal:
            self.renderer.draw_line_3d(
                [car_location.x, car_location.y, 0],
                [impact_projection.x, impact_projection.y, 0],
                self.renderer.yellow())
        if not demoing:
            self.renderer.clear_screen(Demolition.get_render_name(self))
        if park_car:
            self.renderer.draw_string_2d(20, 140, 2, 2, "Parking!",
                                         self.renderer.yellow())
        self.renderer.end_rendering()

        if self.zero_two is not None and self.time > 10.0:
            self.zero_two.render(self.renderer)

        # Choose whether to drive backwards or not
        wall_touch = (distance_from_wall(impact.flatten()) < 500
                      and team_sign * impact.y < 4000)
        local = rotation_matrix.dot(
            Vector3(car_to_destination.x, car_to_destination.y,
                    (impact.z if wall_touch else 17.010000228881836) -
                    self.car.physics.location.z))
        self.steer_correction_radians = math.atan2(local.y, local.x)
        slow_down = park_car or (abs(self.steer_correction_radians > 0.2)
                                 and inside_turning_radius(
                                     local, car_velocity.length)
                                 and obey_turning_radius)
        if slow_down:
            self.renderer.begin_rendering()
            self.renderer.draw_string_2d(
                triforce(20, 50), triforce(10, 20), 6, 6, 'Uh Oh',
                self.renderer.pink() if
                (self.time % 0.5) < 0.25 else self.renderer.red())
            self.renderer.end_rendering()
        turning_radians = self.steer_correction_radians
        backwards = (math.cos(turning_radians) < 0 and not demoing
                     and not self.car.is_super_sonic and not slow_down)
        if backwards:
            turning_radians = invert_angle(turning_radians)
        throttle_sign = (-1 if backwards else 1)

        # Speed control
        target_velocity = (((bounce_location - car_location).length /
                            time) if time > 0 else 2300)
        velocity_change = (target_velocity - car_velocity.flatten().length)
        if park_car:
            self.controller.boost = False
            self.controller.throttle = clamp11(
                (destination - car_location).length / 2000) * throttle_sign
        elif slow_down:
            self.controller.boost = False
            if car_velocity.length > 400:
                self.controller.throttle = -sign(
                    math.cos(
                        car_direction.correction_to(car_velocity.flatten())))
            else:
                self.controller.throttle = 0
        elif (velocity_change > 300 or target_velocity > 1410 or demoing):
            self.controller.boost = (abs(turning_radians) < 0.2
                                     and not self.car.is_super_sonic
                                     and not backwards)
            if self.controller.boost and self.mode is Gamemode.DROPSHOT:
                self.controller.boost = (
                    30 < self.car.boost - (2200 - car_velocity.length) /
                    ((911 + (2 / 3)) if self.car.has_wheel_contact else 1000) *
                    (100 / 3))
            self.controller.throttle = throttle_sign
        elif velocity_change > -150:
            self.controller.boost = False
            self.controller.throttle = 0
        else:
            self.controller.boost = False
            self.controller.throttle = -throttle_sign

        # Steering
        turn = clamp11(turning_radians * 4)
        self.controller.steer = turn
        self.controller.handbrake = (abs(turning_radians) > 1.1
                                     and not self.car.is_super_sonic
                                     and not (slow_down or park_car))

        # Dodging
        self.controller.jump = False
        dodge_for_speed = (velocity_change > 500 and not backwards
                           and self.car.boost < 14
                           and self.time > self.next_dodge_time + 0.85
                           and car_to_destination.size >
                           (2000 if take_serious_shot else 1200)
                           and abs(turning_radians) < 0.1 and not demoing)
        if (((car_to_ball.flatten().size < 400 and ball_location.z < 300)
             or dodge_for_speed) and car_velocity.size > 1100) or self.dodging:
            dodge_at_ball = (impact_time < 0.4)
            dodge_angle = (car_direction.correction_to(car_to_ball)
                           if dodge_at_ball else
                           car_direction.correction_to(car_to_destination))
            dodge(self, dodge_angle, rotation_velocity,
                  4 if dodge_at_ball else 1)

        # Half-flips
        if backwards and not park_car and (
                time if wait else
                impact_time) > 0.6 and car_velocity.size > 900 and abs(
                    turning_radians) < 0.1 or self.halfflipping:
            halfflip(self, rotation_velocity)

        # Recovery
        if not (self.dodging or self.halfflipping):
            if not self.car.has_wheel_contact:
                self.controller.steer = 0
                recover(self,
                        rotation_velocity,
                        allow_yaw_wrap=car_location.z > 250)
                self.controller.boost = False
            else:
                self.controller.roll = 0
                self.controller.pitch = 0
                self.controller.yaw = 0

        return self.controller
Beispiel #4
0
    def get_output(self, packet: GameTickPacket) -> SimpleControllerState:
        self.quick_chat_handler.handle_quick_chats(packet)
        if self.enable_goal_music:
            self.jukebox.update(packet)

        # Collect data from the packet
        self.mode = (Gamemode.DROPSHOT if self.get_field_info().num_boosts == 0
                     else Gamemode.SOCCAR)
        self.time = packet.game_info.seconds_elapsed
        ball_location = Vector3(packet.game_ball.physics.location)
        self.car = packet.game_cars[self.index]
        car_location = Vector3(self.car.physics.location)
        car_velocity = Vector3(self.car.physics.velocity)
        car_direction = get_car_facing_vector(self.car)
        self.car_direction = car_direction
        car_to_ball = ball_location - car_location
        is_1v1 = (packet.num_cars == 2
                  and packet.game_cars[not self.index].team != self.car.team)
        team_sign = (1 if self.car.team == (
            is_1v1 and packet.teams[self.car.team].score == 7
            and packet.teams[not self.car.team].score == 0) else -1)
        enemy_goal = Vector2(0, team_sign * 5120)
        kickoff = packet.game_info.is_kickoff_pause
        ball_prediction = self.get_ball_prediction_struct()
        impacts = [
            get_impact(ball_prediction, car, self.time, self.renderer)
            for car in packet.game_cars[:packet.num_cars]
        ]
        impact, impact_time = impacts[self.index]
        self.impact = impact
        impact_projection = project_to_wall(car_location,
                                            impact.flatten() - car_location)
        rotation_matrix = Matrix3D([
            self.car.physics.rotation.pitch,
            self.car.physics.rotation.yaw,
            self.car.physics.rotation.roll,
        ])
        self.rotation_matrix = rotation_matrix
        rotation_velocity = rotation_matrix.dot(
            self.car.physics.angular_velocity)
        self.rotation_velocity = rotation_velocity
        car_local_velocity = rotation_matrix.dot(car_velocity)
        correct_side_of_ball: bool = (
            (impact_projection.y - car_location.y) * team_sign > 0)
        # Hi robbie!
        if self.time == packet.game_ball.latest_touch.time_seconds and self.enable_ball_touch_audio:
            print(
                "\a"
            )  # for all the people who mute anarchy because they dont like 'boing' :)

        if self.render_disco_ball:
            # this kinda kills the anime and idk what to do about it
            self.renderer.begin_rendering("disco")
            for i in range(100):
                hmmm = ball_location + Vector3(
                    random.randint(-1000, 1000),
                    random.randint(-1000, 1000),
                    random.randint(-1000, 1000),
                )
                self.renderer.draw_line_3d(
                    [ball_location.x, ball_location.y, ball_location.z],
                    [hmmm.x, hmmm.y, hmmm.z],
                    self.renderer.create_color(
                        255,
                        random.randint(0, 255),
                        random.randint(0, 255),
                        random.randint(0, 255),
                    ),
                )
            self.renderer.end_rendering()

        # Action.
        if self.action:
            print(self.action.__class__.__name__)
            if not hasattr(self.action,
                           "finished") or not self.action.finished:
                return self.action.step(packet)
            self.action = None

        # Handle bouncing
        ball_bounces: List[Slice] = get_ball_bounces(
            self.get_ball_prediction_struct())
        bounce_location = None
        for b in ball_bounces:
            time: float = b.game_seconds - self.time
            if time < impact_time - 0.3:
                continue
            bounce_location: Vector2 = Vector2(b.physics.location)
            break
        if bounce_location is None:
            time = 0

        # Handle aerials
        if self.aerial is not None:
            if self.car.has_wheel_contact and self.aerial.jt + 0.5 < self.time:
                # Give up on an aerial
                self.aerial = None
            else:
                # Get the output of the aerial
                aerial_output = self.aerial.execute(
                    packet, self.index, self.get_ball_prediction_struct())
                if self.aerial.target is not None:
                    self.renderer.begin_rendering()
                    self.renderer.draw_line_3d(
                        car_location,
                        car_location + self.aerial.target,
                        self.renderer.white(),
                    )
                    self.renderer.end_rendering()
                return aerial_output
        if (self.aerial is None and self.car.has_wheel_contact
                and impact.z - self.car.physics.location.z > 500
                and car_velocity.length > 100 and self.demo is None
                and (self.car.boost > 20 or
                     (abs(time - 0.6) < 0.1 and
                      (impact - car_location).flatten().length < 1000))
                and abs(self.steer_correction_radians) < 0.4
                and impact_time < 2.5
                and (impact_projection.y * team_sign > -5000
                     or abs(impact_projection.x) > 1000) and
                math.cos(car_velocity.angle_between(impact - car_location)) *
                car_velocity.flatten().length > 400):
            # Start a new aerial
            self.aerial = Aerial(self.time)
            return self.aerial.execute(packet, self.index,
                                       self.get_ball_prediction_struct())

        # Set a destination for Anarchy to reach
        teammate_going_for_ball: bool = False
        for index, car in enumerate(packet.game_cars[:packet.num_cars]):
            if car.team == self.team and index != self.index:
                teammate_location = Vector3(car.physics.location)
                teammate_correct_side_of_ball: bool = (
                    (impacts[index][0].y - teammate_location.y) * team_sign >
                    0)
                if correct_side_of_ball and not teammate_correct_side_of_ball:
                    continue
                if impacts[index][1] + 0.1 < impact_time or (
                        not correct_side_of_ball
                        and teammate_correct_side_of_ball):
                    teammate_going_for_ball = True
                    self.renderer.begin_rendering("teammate")
                    self.renderer.draw_line_3d(car_location, teammate_location,
                                               self.renderer.black())
                    self.renderer.draw_rect_3d(teammate_location, 4, 4, True,
                                               self.renderer.black())
                    self.renderer.end_rendering()
                    break

        avoid_own_goal = impact_projection.y * team_sign < -5000
        wait = ball_location.z > 200 and self.car.physics.location.z < 200
        take_serious_shot = False
        obey_turning_radius = True  # Slow down if the target is in the turning radius
        demoing = self.demo is not None
        close_boost = closest_boost(
            Vector3(-impact.x, impact.y, 0) if teammate_going_for_ball
            and impact_time < 2.5 and not kickoff else car_location,
            self.get_field_info().boost_pads,
            packet.game_boosts,
        )
        convenient_boost = (close_boost
                            and (close_boost - car_location).flatten().length *
                            (5 if is_1v1 else 3) <
                            (impact - car_location).flatten().length)
        need_boost = not self.car.is_super_sonic and self.car.boost < (
            40 if convenient_boost else 30)
        park_car = False
        not_our_kickoff = kickoff and teammate_going_for_ball

        if wait and bounce_location is not None:
            destination = Vector3(bounce_location.x, bounce_location.y, 0)
        else:
            destination = impact
            time = 0
        if kickoff and not not_our_kickoff:
            destination = ball_location + Vector3(0, -92.75 * team_sign, 0)
            obey_turning_radius = False
        elif (avoid_own_goal and not kickoff and not demoing and
              (not teammate_going_for_ball or impact.y * team_sign > -4000)):
            offset = max(0, impact_time - 0.5) * 160 + 110
            destination += Vector2(offset * -sign(impact_projection.x),
                                   (140 if wait else 0) * -team_sign)
        elif (not_our_kickoff or teammate_going_for_ball or demoing
              or (close_boost and need_boost and
                  (not is_1v1 or convenient_boost))):
            if close_boost:
                destination = close_boost

            demo_location = None
            if (demoing or not need_boost) and not kickoff:
                if not demoing:
                    self.demo = Demolition.start_demo(self, packet)
                demo_location, demo_time = (self.demo.get_destination(packet)
                                            if self.demo is not None else
                                            (None, 0))
                if demo_location is not None:
                    destination = demo_location
                    obey_turning_radius = False
                    demoing = True
                    time = 0
            if demo_location is None:
                self.demo = None
                demoing = False
                if ((not need_boost and car_to_ball.length > 2500)
                        if not not_our_kickoff else
                    (abs(car_location.x) < 50)):  # Middle
                    park_car = True
                    destination = Vector2(0, -4900 * team_sign)
                    destination += (impact.flatten() - destination) / 8
                    destination = Vector3(destination.x, destination.y, 17)
        elif team_sign * (car_location.y - ball_location.y) > 0:
            destination.x += max(car_to_ball.length / 3, 80
                                 if wait else 120) * -sign(impact_projection.x)
        else:
            destination += (destination - enemy_goal).normalized * max(
                (destination - car_location).length /
                ((2.1 + impact_time / 1.9) if take_serious_shot else 3.7),
                60 if wait else 110,
            )
            take_serious_shot = True
        if abs(car_location.y) > 5120:
            destination.x = min(700,
                                max(-700,
                                    destination.x))  # Don't get stuck in goal
        car_to_destination = destination - car_location

        # Rendering
        self.renderer.begin_rendering()
        if kickoff:
            self.renderer.draw_string_2d(20, 140, 2, 2, "Kickoff",
                                         self.renderer.white())
        if take_serious_shot:
            self.renderer.draw_string_2d(20, 140, 2, 2, "Shoot",
                                         self.renderer.white())
        self.renderer.draw_line_3d(
            [destination.x, destination.y, impact.z],
            [impact.x, impact.y, impact.z],
            self.renderer.blue(),
        )
        if avoid_own_goal:
            self.renderer.draw_line_3d(
                [car_location.x, car_location.y, 0],
                [impact_projection.x, impact_projection.y, 0],
                self.renderer.yellow(),
            )
        if not demoing:
            self.renderer.clear_screen(Demolition.get_render_name(self))
        if park_car:
            self.renderer.draw_string_2d(20, 140, 2, 2, "Parking!",
                                         self.renderer.yellow())
        self.renderer.end_rendering()

        if self.zero_two is not None and self.time > 10.0:
            self.zero_two.render(self.renderer)

        # Choose whether to drive backwards or not
        wall_touch = (distance_from_wall(impact.flatten()) < 500
                      and team_sign * impact.y < 4000)
        local = rotation_matrix.dot(
            Vector3(
                car_to_destination.x,
                car_to_destination.y,
                (impact.z if wall_touch else 17.010000228881836) -
                self.car.physics.location.z,
            ))
        self.steer_correction_radians = math.atan2(local.y, local.x)
        slow_down = (abs(self.steer_correction_radians) > 0.2
                     and inside_turning_radius(local, car_velocity.length)
                     and obey_turning_radius)
        if slow_down:
            self.renderer.begin_rendering()
            self.renderer.draw_string_2d(
                triforce(20, 50),
                triforce(10, 20),
                6,
                6,
                "Uh Oh",
                self.renderer.pink() if
                (self.time % 0.5) < 0.25 else self.renderer.red(),
            )
            self.renderer.end_rendering()
        turning_radians = self.steer_correction_radians
        backwards = (math.cos(turning_radians) < 0 and not demoing
                     and not self.car.is_super_sonic)
        if backwards:
            turning_radians = invert_angle(turning_radians)
        throttle_sign = -1 if backwards else 1

        # Speed control
        target_velocity = (((bounce_location - car_location).length /
                            time) if time > 0 else 2300)
        velocity_change = target_velocity - car_velocity.flatten().length
        if park_car:
            self.controller.boost = False
            self.controller.throttle = (clamp11(
                (destination - car_location).length / 2000) * throttle_sign)
        elif slow_down:
            if car_velocity.length > 400:
                self.controller.throttle = -sign(
                    rotation_matrix.data[0].dot(destination - car_location))
                self.controller.boost = self.controller.throttle > 0
            else:
                self.controller.throttle = 0
        elif velocity_change > 300 or target_velocity > 1410 or demoing:
            self.controller.boost = (abs(turning_radians) < 0.2
                                     and not self.car.is_super_sonic
                                     and not backwards)
            if self.controller.boost and self.mode is Gamemode.DROPSHOT:
                self.controller.boost = 30 < self.car.boost - (
                    2200 - car_velocity.length) / (
                        (911 + (2 / 3))
                        if self.car.has_wheel_contact else 1000) * (100 / 3)
            self.controller.throttle = throttle_sign
        elif velocity_change > -150:
            self.controller.boost = False
            self.controller.throttle = 0
        else:
            self.controller.boost = False
            self.controller.throttle = -throttle_sign

        # Steering
        turn = clamp11(turning_radians * 4)
        self.controller.steer = turn
        self.controller.handbrake = (abs(turning_radians) > 1.1
                                     and not self.car.is_super_sonic
                                     and not (slow_down or park_car))

        # prevent inefficient powerslides
        if sign(rotation_velocity.z) != sign(self.controller.steer) or sign(
                car_local_velocity.x) != sign(self.controller.throttle):
            self.controller.handbrake = False

        # Dodging
        self.controller.jump = False
        dodge_for_speed = (velocity_change > 500 and not backwards
                           and self.car.boost < 14
                           and self.time > self.next_dodge_time + 0.85
                           and car_to_destination.size >
                           (2000 if take_serious_shot else 1200)
                           and abs(turning_radians) < 0.1 and not demoing)
        if kickoff:
            dodge_for_speed = car_velocity.size > 1250
        if (((car_to_ball.flatten().size < 400 and ball_location.z < 300)
             or dodge_for_speed) and car_velocity.size > 1100) or self.dodging:
            dodge_at_ball = impact_time < 0.4
            dodge_direction = car_to_ball if dodge_at_ball else car_to_destination
            self.action = Dodge(self, dodge_direction)
            return self.action.step(packet)

        # Half-flips
        if (backwards and not park_car and
            (time if wait else impact_time) > 0.6 and car_velocity.size > 900
                and abs(turning_radians) < 0.1 or self.halfflipping):
            halfflip(self, rotation_velocity)

        # Recovery
        if not (self.dodging
                or self.halfflipping) and not self.car.has_wheel_contact:
            self.action = Recover(self,
                                  rotation_velocity,
                                  allow_yaw_wrap=(car_location.z > 250))
            return self.action.step(packet)

        return self.controller