class DoubleTouch(Maneuver): """ Execute a regular AerialStrike, but when it finishes, look if we could continue aerialing for a second hit. """ def __init__(self, aerial_strike: AerialStrike): super().__init__(aerial_strike.car) self.aerial_strike = aerial_strike self.info = self.aerial_strike.info self.aerial = Aerial(self.car) self.aerial.up = vec3(0, 0, -1) self._flight_path: List[vec3] = [] def find_second_touch(self): self.info.predict_ball(duration=4.0) for i in range(0, len(self.info.ball_predictions), 5): ball = self.info.ball_predictions[i] if ball.position[2] < 500: break self.aerial.target = ball.position - direction( ball, self.aerial_strike.target) * 80 self.aerial.arrival_time = ball.time final_car = AerialStrike.simulate_flight(self.car, self.aerial, self._flight_path) if distance(final_car, self.aerial.target) < 50: return self.finished = True def step(self, dt: float): if self.aerial_strike.finished: self.aerial.step(dt) self.controls = self.aerial.controls self.finished = self.aerial.finished if self.car.on_ground: self.finished = True else: self.aerial_strike.step(dt) self.controls = self.aerial_strike.controls if self.aerial_strike.finished: if not self.car.on_ground: self.find_second_touch() else: self.finished = True def interruptible(self) -> bool: return self.aerial_strike.interruptible() def render(self, draw: DrawingTool): if self.aerial_strike.finished: draw.color(draw.pink) draw.crosshair(self.aerial.target) draw.color(draw.lime) draw.polyline(self._flight_path) else: self.aerial_strike.render(draw)
def aerial_step(aerial: Aerial, car: Car, rotation_input=None, dt=1.0 / 60.0): return_value = aerial.step(dt) if rotation_input is not None and aerial.arrival_time - car.time <= 0.5: aerial.controls.pitch = rotation_input.pitch aerial.controls.yaw = rotation_input.yaw aerial.controls.roll = rotation_input.roll # aerial.controls.boost = rotation_input.boost return return_value
class DoubleTouch(Maneuver): """ Execute a regular AerialStrike, but when it finishes, look if we could continue aerialing for a second hit. """ def __init__(self, aerial_strike: AerialStrike): super().__init__(aerial_strike.car) self.aerial_strike = aerial_strike self.info = self.aerial_strike.info self.aerial = Aerial(self.car) def find_second_touch(self): self.info.predict_ball(time_limit=3.0) intercept = AirToAirIntercept(self.car, self.info.ball_predictions) self.aerial.target = intercept.position - direction(intercept, self.aerial_strike.target) * 80 self.aerial.up = vec3(0, 0, -1) self.aerial.arrival_time = intercept.time if not intercept.is_viable: self.finished = True def step(self, dt: float): if self.aerial_strike.finished: self.aerial.step(dt) self.controls = self.aerial.controls self.finished = self.aerial.finished else: self.aerial_strike.step(dt) self.controls = self.aerial_strike.controls if self.aerial_strike.finished: if not self.car.on_ground: self.find_second_touch() else: self.finished = True def interruptible(self) -> bool: return self.aerial_strike.interruptible() def render(self, draw: DrawingTool): if self.aerial_strike.finished: draw.color(draw.pink) draw.crosshair(self.aerial.target) else: self.aerial_strike.render(draw)
def simulate_flight(car: Car, aerial: Aerial, flight_path: List[vec3] = None) -> Car: test_car = Car(car) test_aerial = Aerial(test_car) test_aerial.target = aerial.target test_aerial.arrival_time = aerial.arrival_time test_aerial.angle_threshold = aerial.angle_threshold test_aerial.up = aerial.up test_aerial.single_jump = aerial.single_jump if flight_path: flight_path.clear() while not test_aerial.finished: test_aerial.step(1 / 120) test_car.boost = 100 # TODO: fix boost depletion in RLU car sim test_car.step(test_aerial.controls, 1 / 120) if flight_path: flight_path.append(vec3(test_car.position)) return test_car
def simulate_flight(self, car: Car, write_to_flight_path=True) -> Car: test_car = Car(car) test_aerial = Aerial(test_car) test_aerial.target = self.aerial.target test_aerial.arrival_time = self.aerial.arrival_time test_aerial.angle_threshold = self.aerial.angle_threshold test_aerial.up = self.aerial.up test_aerial.single_jump = self.aerial.single_jump if write_to_flight_path: self._flight_path.clear() while not test_aerial.finished: test_aerial.step(1 / 120) test_car.boost = 100 # TODO: fix boost depletion in RLU car sim test_car.step(test_aerial.controls, 1 / 120) if write_to_flight_path: self._flight_path.append(vec3(test_car.position)) return test_car
class AerialStrike(Strike): MAX_DISTANCE_ERROR = 50 DELAY_TAKEOFF = True MINIMAL_HEIGHT = 400 MAXIMAL_HEIGHT = 800 MINIMAL_HEIGHT_TIME = 1.0 MAXIMAL_HEIGHT_TIME = 2.5 DOUBLE_JUMP = False def __init__(self, car: Car, info: GameInfo, target: vec3 = None): self.aerial = Aerial(car) self.aerial.angle_threshold = 0.8 self.aerial.single_jump = not self.DOUBLE_JUMP super().__init__(car, info, target) self.arrive.allow_dodges_and_wavedashes = False self.aerialing = False self.too_early = False self._flight_path: List[vec3] = [] def intercept_predicate(self, car: Car, ball: Ball): required_time = range_map(ball.position[2], self.MINIMAL_HEIGHT, self.MAXIMAL_HEIGHT, self.MINIMAL_HEIGHT_TIME, self.MAXIMAL_HEIGHT_TIME) return self.MINIMAL_HEIGHT < ball.position[ 2] < self.MAXIMAL_HEIGHT and ball.time - car.time > required_time def configure(self, intercept: Intercept): super().configure(intercept) self.aerial.target = intercept.position - direction( intercept, self.target) * 80 self.aerial.up = normalize( ground_direction(intercept, self.car) + vec3(0, 0, 0.5)) self.aerial.arrival_time = intercept.time def simulate_flight(self, car: Car, write_to_flight_path=True) -> Car: test_car = Car(car) test_aerial = Aerial(test_car) test_aerial.target = self.aerial.target test_aerial.arrival_time = self.aerial.arrival_time test_aerial.angle_threshold = self.aerial.angle_threshold test_aerial.up = self.aerial.up test_aerial.single_jump = self.aerial.single_jump if write_to_flight_path: self._flight_path.clear() while not test_aerial.finished: test_aerial.step(1 / 120) test_car.boost = 100 # TODO: fix boost depletion in RLU car sim test_car.step(test_aerial.controls, 1 / 120) if write_to_flight_path: self._flight_path.append(vec3(test_car.position)) return test_car def interruptible(self) -> bool: return self.aerialing or super().interruptible() def step(self, dt): if self.aerialing: self.aerial.target_orientation = look_at( direction(self.car, self.target), vec3(0, 0, -1)) self.aerial.step(dt) self.controls = self.aerial.controls self.finished = self.aerial.finished else: super().step(dt) # simulate aerial from current state simulated_car = self.simulate_flight(self.car) # if the car ended up too far, we're too fast and we need to slow down if ground_distance(self.car, self.aerial.target) + 100 < ground_distance( self.car, simulated_car): # self.controls.throttle = -1 pass # if it ended up near the target, we could take off elif distance(simulated_car, self.aerial.target) < self.MAX_DISTANCE_ERROR: if angle_to(self.car, self.aerial.target) < 0.1 or norm( self.car.velocity) < 1000: if self.DELAY_TAKEOFF: # extrapolate current state a small amount of time future_car = Car(self.car) time = 0.2 future_car.time += time displacement = future_car.velocity * time if norm(future_car.velocity) > 500\ else normalize(future_car.velocity) * 500 * time future_car.position += displacement # simulate aerial fot the extrapolated car again future_simulated_car = self.simulate_flight( future_car, write_to_flight_path=False) # if the aerial is also successful, that means we should continue driving instead of taking off # this makes sure that we go for the most late possible aerials, which are the most effective if distance( future_simulated_car, self.aerial.target) > self.MAX_DISTANCE_ERROR: self.aerialing = True else: self.too_early = True else: self.aerialing = True else: # self.controls.boost = True self.controls.throttle = 1 def render(self, draw: DrawingTool): super().render(draw) draw.color(draw.lime if self.aerialing else ( draw.orange if self.too_early else draw.red)) draw.polyline(self._flight_path)
class AerialHandler(): def __init__(self, agent): self.agent = agent self.active = False self.timer = time.time() self.setup() self.threshold = 300 def setup(self): self.aerial = Aerial(self.agent.game.my_car) self.turn = AerialTurn(self.agent.game.my_car) myGoal = center = Vector([0, 5120 * sign(self.agent.team), 200]) enemyGoal = center = Vector([0, 5120 * -sign(self.agent.team), 200]) aboveThreshold = False if self.agent.me.boostLevel > 0: for i in range(0, self.agent.ballPred.num_slices): targetVec = Vector([ self.agent.ballPred.slices[i].physics.location.x, self.agent.ballPred.slices[i].physics.location.y, self.agent.ballPred.slices[i].physics.location.z ]) if self.agent.ballPred.slices[i].physics.location.z >= 300: if not aboveThreshold: aboveThreshold = True goalDist = distance2D(center, targetVec) acceptable = False if self.agent.team == 0: if self.agent.me.location[1] < targetVec[1]: acceptable = True else: if self.agent.me.location[1] > targetVec[1]: acceptable = True if acceptable: zOffset = -10 if goalDist < 1500: if targetVec[2] > 600: zOffset = 70 shotAngle = correctAngle( math.degrees(angle2(targetVec, enemyGoal)) + 90 * -sign(self.agent.team)) if self.agent.team == 0: if targetVec[1] >= 0: if abs(targetVec[0]) > 893: if targetVec[0] > 0: closePost = Vector([ 893, 5120 * -sign(self.agent.team), 200 ]) else: closePost = Vector([ -893, 5120 * -sign(self.agent.team), 200 ]) attackAngle = correctAngle( math.degrees( angle2(targetVec, closePost)) + 90 * -sign(self.agent.team)) targetLocal = toLocal( Vector([ self.agent.ballPred.slices[i]. physics.location.x, self.agent.ballPred.slices[i]. physics.location.y, self.agent.ballPred.slices[i]. physics.location.z ]), self.agent.me) carToBallAngle = correctAngle( math.degrees( math.atan2(targetLocal[1], targetLocal[0]))) totalAngle = correctAngle( math.degrees( math.tan(attackAngle) + math.tan(carToBallAngle) / (1 - (math.tan(attackAngle) * math.tan(carToBallAngle))))) if abs(totalAngle) > 60: continue elif self.agent.team == 1: if targetVec[1] <= 0: if abs(targetVec[0]) > 893: if targetVec[0] > 0: closePost = Vector([ 893, 5120 * -sign(self.agent.team), 200 ]) else: closePost = Vector([ -893, 5120 * -sign(self.agent.team), 200 ]) attackAngle = correctAngle( math.degrees( angle2(targetVec, closePost)) + 90 * -sign(self.agent.team)) targetLocal = toLocal( Vector([ self.agent.ballPred.slices[i]. physics.location.x, self.agent.ballPred.slices[i]. physics.location.y, self.agent.ballPred.slices[i]. physics.location.z ]), self.agent.me) carToBallAngle = correctAngle( math.degrees( math.atan2(targetLocal[1], targetLocal[0]))) totalAngle = correctAngle(carToBallAngle + attackAngle) if abs(totalAngle) > 60: continue if abs(shotAngle) <= 75: xOffset = clamp(80, -80, (shotAngle * 2) * -sign(self.agent.team)) self.aerial.target = vec3( self.agent.ballPred.slices[i].physics.location. x + xOffset, self.agent.ballPred.slices[i]. physics.location.y, self.agent.ballPred. slices[i].physics.location.z + zOffset) self.aerial.arrival_time = self.agent.ballPred.slices[ i].game_seconds simulation = self.aerial.simulate() if norm(simulation.location - self.aerial.target) < 100: self.target_ball = self.agent.ballPred.slices[ i] self.xOffset = xOffset self.zOffset = zOffset self.active = True if self.agent.onSurface: if self.agent.ballPred.slices[ i].physics.location.z >= 400: targetLocal = toLocal( Vector([ self.agent.ballPred.slices[i]. physics.location.x + xOffset, self.agent.ballPred.slices[i]. physics.location.y, self.agent.ballPred.slices[i]. physics.location.z + zOffset ]), self.agent.me) carToBallAngle = correctAngle( math.degrees( math.atan2( targetLocal[1], targetLocal[0]))) if abs(carToBallAngle) < 45: if distance2D( self.agent.me.location, targetLocal) > 1500: if self.agent.ballPred.slices[ i].physics.location.z >= 900: self.agent.activeState = airLaunch( self.agent) self.active = False return self.agent.activeState.update( ) break else: if aboveThreshold: break def stillValid(self): for i in range(0, self.agent.ballPred.num_slices): if self.agent.ballPred.slices[i].physics.location.z >= 300: if abs(self.target_ball.game_seconds - self.agent.ballPred.slices[i].game_seconds ) < self.agent.deltaTime * 3: difference = 0 difference += abs( (self.agent.ballPred.slices[i].physics.location.x + self.xOffset) - (self.target_ball.physics.location.x + self.zOffset)) difference += abs( self.agent.ballPred.slices[i].physics.location.y - self.target_ball.physics.location.y) difference += abs( (self.agent.ballPred.slices[i].physics.location.z + self.zOffset) - (self.target_ball.physics.location.z + self.zOffset)) if difference < 10: self.aerial.target = vec3( self.agent.ballPred.slices[i].physics.location.x + self.xOffset, self.agent.ballPred.slices[i].physics.location.y, self.agent.ballPred.slices[i].physics.location.z + self.zOffset) self.aerial.arrival_time = self.agent.ballPred.slices[ i].game_seconds self.target_ball = self.agent.ballPred.slices[i] simulation = self.aerial.simulate() return self.active = False self.setup() def update(self): if self.agent.me.boostLevel > 0: self.setup() self.aerial.step(self.agent.deltaTime) self.controls = self.aerial.controls self.controls.jump = True if self.aerial.finished: self.active = False else: self.active = False if time.time() - self.timer > 0.5: if self.agent.onSurface: self.active = False return self.controls
class Agent(BaseAgent): def __init__(self, name, team, index): Game.set_mode("soccar") self.game = Game(index, team) self.controls = SimpleControllerState() self.timer = 0.0 self.timeout = 5.0 self.aerial = None self.turn = None self.state = State.RESET self.ball_predictions = None self.target_ball = None self.log = open("../../analysis/aerial/info.ndjson", "w") def get_output(self, packet: GameTickPacket) -> SimpleControllerState: self.game.read_game_information(packet, self.get_rigid_body_tick(), self.get_field_info()) self.controls = SimpleControllerState() next_state = self.state if self.state == State.RESET: self.timer = 0.0 b_position = Vector3(random.uniform(-1500, 1500), random.uniform( 2500, 3500), random.uniform( 300, 500)) b_velocity = Vector3(random.uniform(-300, 300), random.uniform(-100, 100), random.uniform(1000, 1500)) ball_state = BallState(physics=Physics( location=b_position, velocity=b_velocity, rotation=Rotator(0, 0, 0), angular_velocity=Vector3(0, 0, 0) )) # this just initializes the car and ball # to different starting points each time c_position = Vector3(b_position.x, 0 * random.uniform(-1500, -1000), 25) #c_position = Vector3(200, -1000, 25) car_state = CarState(physics=Physics( location=c_position, velocity=Vector3(0, 800, 0), rotation=Rotator(0, 1.6, 0), angular_velocity=Vector3(0, 0, 0) ), boost_amount=100) self.set_game_state(GameState( ball=ball_state, cars={self.game.id: car_state}) ) next_state = State.WAIT if self.state == State.WAIT: if self.timer > 0.2: next_state = State.INITIALIZE if self.state == State.INITIALIZE: self.aerial = Aerial(self.game.my_car) # self.aerial.up = normalize(vec3( # random.uniform(-1, 1), # random.uniform(-1, 1), # random.uniform(-1, 1))) self.turn = AerialTurn(self.game.my_car) # predict where the ball will be prediction = Ball(self.game.ball) self.ball_predictions = [vec3(prediction.location)] for i in range(100): prediction.step(0.016666) prediction.step(0.016666) self.ball_predictions.append(vec3(prediction.location)) # if the ball is in the air if prediction.location[2] > 500: self.aerial.target = prediction.location self.aerial.arrival_time = prediction.time simulation = self.aerial.simulate() # # check if we can reach it by an aerial if norm(simulation.location - self.aerial.target) < 100: prediction.step(0.016666) prediction.step(0.016666) self.aerial.target = prediction.location self.aerial.arrival_time = prediction.time self.target_ball = Ball(prediction) break next_state = State.RUNNING if self.state == State.RUNNING: self.log.write(f"{{\"car\":{self.game.my_car.to_json()}," f"\"ball\":{self.game.ball.to_json()}}}\n") self.aerial.step(self.game.time_delta) self.controls = self.aerial.controls if self.timer > self.timeout: next_state = State.RESET self.aerial = None self.turn = None self.timer += self.game.time_delta self.state = next_state self.renderer.begin_rendering() red = self.renderer.create_color(255, 230, 30, 30) purple = self.renderer.create_color(255, 230, 30, 230) white = self.renderer.create_color(255, 230, 230, 230) if self.aerial: r = 200 x = vec3(r, 0, 0) y = vec3(0, r, 0) z = vec3(0, 0, r) target = self.aerial.target self.renderer.draw_line_3d(target - x, target + x, purple) self.renderer.draw_line_3d(target - y, target + y, purple) self.renderer.draw_line_3d(target - z, target + z, purple) if self.ball_predictions: self.renderer.draw_polyline_3d(self.ball_predictions, purple) self.renderer.end_rendering() return self.controls
class AerialStrike(Strike): MAX_DISTANCE_ERROR = 50 DELAY_TAKEOFF = True MINIMAL_HEIGHT = 500 MAXIMAL_HEIGHT = 800 MINIMAL_HEIGHT_TIME = 0.8 MAXIMAL_HEIGHT_TIME = 1.5 DOUBLE_JUMP = False def __init__(self, car: Car, info: GameInfo, target: vec3 = None): self.aerial = Aerial(car) self.aerial.angle_threshold = 0.8 self.aerial.double_jump = self.DOUBLE_JUMP super().__init__(car, info, target) self.arrive.allow_dodges_and_wavedashes = False self.aerialing = False self.too_early = False self._flight_path: List[vec3] = [] def intercept_predicate(self, car: Car, ball: Ball): required_time = range_map(ball.position[2], self.MINIMAL_HEIGHT, self.MAXIMAL_HEIGHT, self.MINIMAL_HEIGHT_TIME, self.MAXIMAL_HEIGHT_TIME) return self.MINIMAL_HEIGHT < ball.position[ 2] < self.MAXIMAL_HEIGHT and ball.time - car.time > required_time def configure(self, intercept: Intercept): super().configure(intercept) self.aerial.target_position = intercept.position - direction( intercept, self.target) * 100 self.aerial.up = normalize( ground_direction(intercept, self.car) + vec3(0, 0, 0.5)) self.aerial.arrival_time = intercept.time @staticmethod def simulate_flight(car: Car, aerial: Aerial, flight_path: List[vec3] = None) -> Car: test_car = Car(car) test_aerial = Aerial(test_car) test_aerial.target_position = aerial.target_position test_aerial.arrival_time = aerial.arrival_time test_aerial.angle_threshold = aerial.angle_threshold test_aerial.up = aerial.up test_aerial.double_jump = aerial.double_jump if flight_path: flight_path.clear() while not test_aerial.finished: test_aerial.step(1 / 120) test_car.boost = 100 # TODO: fix boost depletion in RLU car sim test_car.step(test_aerial.controls, 1 / 120) if flight_path: flight_path.append(vec3(test_car.position)) return test_car def interruptible(self) -> bool: return self.aerialing or super().interruptible() def step(self, dt): time_left = self.aerial.arrival_time - self.car.time if self.aerialing: to_ball = direction(self.car, self.info.ball) # freestyling if self.car.position[2] > 200: # if time_left > 0.7: # rotation = axis_to_rotation(self.car.forward() * 0.5) # self.aerial.up = dot(rotation, self.car.up()) # else: self.aerial.up = vec3(0, 0, -1) + xy(to_ball) self.aerial.target_orientation = look_at(to_ball, vec3(0, 0, -3) + to_ball) self.aerial.step(dt) self.controls = self.aerial.controls self.finished = self.aerial.finished and time_left < -0.3 else: super().step(dt) # simulate aerial from current state simulated_car = self.simulate_flight(self.car, self.aerial, self._flight_path) speed_towards_target = dot( self.car.velocity, ground_direction(self.car, self.aerial.target_position)) speed_needed = ground_distance( self.car, self.aerial.target_position) / time_left # too fast, slow down if speed_towards_target > speed_needed and angle_to( self.car, self.aerial.target_position) < 0.1: self.controls.throttle = -1 # if it ended up near the target, we could take off elif distance( simulated_car, self.aerial.target_position) < self.MAX_DISTANCE_ERROR: if angle_to(self.car, self.aerial.target_position) < 0.1 or norm( self.car.velocity) < 1000: if self.DELAY_TAKEOFF and ground_distance( self.car, self.aerial.target_position) > 1000: # extrapolate current state a small amount of time future_car = Car(self.car) time = 0.5 future_car.time += time displacement = future_car.velocity * time if norm(future_car.velocity) > 500\ else normalize(future_car.velocity) * 500 * time future_car.position += displacement # simulate aerial fot the extrapolated car again future_simulated_car = self.simulate_flight( future_car, self.aerial) # if the aerial is also successful, that means we should continue driving instead of taking off # this makes sure that we go for the most late possible aerials, which are the most effective if distance(future_simulated_car, self.aerial. target_position) > self.MAX_DISTANCE_ERROR: self.aerialing = True else: self.too_early = True else: self.aerialing = True else: # self.controls.boost = True self.controls.throttle = 1 def render(self, draw: DrawingTool): super().render(draw) draw.color(draw.lime if self.aerialing else ( draw.orange if self.too_early else draw.red)) draw.polyline(self._flight_path)
class AerialHandler(): def __init__(self,agent): self.agent = agent self.active = False self.setup() def setup(self): self.aerial = Aerial(self.agent.game.my_car) self.turn = AerialTurn(self.agent.game.my_car) myGoal = center = Vector([0, 5200 * sign(self.agent.team), 200]) enemyGoal = center = Vector([0, 5200 * -sign(self.agent.team), 200]) if self.agent.me.boostLevel > 0: for i in range(0, self.agent.ballPred.num_slices): targetVec = Vector([self.agent.ballPred.slices[i].physics.location.x, self.agent.ballPred.slices[i].physics.location.y, self.agent.ballPred.slices[i].physics.location.z]) # interferenceTimer += self.agent.deltaTime # if interferenceTimer < 1: # if findEnemyClosestToLocation(self.agent,targetVec)[1] < 150: # break if self.agent.ballPred.slices[i].physics.location.z >= 300: goalDist = distance2D(center, targetVec) #if distance2D(myGoal, targetVec) > distance2D(myGoal, self.agent.me.location): if self.agent.me.location[1] * sign(self.agent.team) > self.agent.ballPred.slices[i].physics.location.y * sign(self.agent.team): zOffset = 0 if goalDist < 1500: if targetVec[2] > 600: zOffset = 70 shotAngle = correctAngle(math.degrees(angle2(targetVec, enemyGoal)) + 90 * -sign(self.agent.team)) if abs(shotAngle) <=80: xOffset = clamp(80,-80,(shotAngle*2)*-sign(self.agent.team)) self.aerial.target = vec3(self.agent.ballPred.slices[i].physics.location.x+xOffset, self.agent.ballPred.slices[i].physics.location.y, self.agent.ballPred.slices[i].physics.location.z+zOffset) self.aerial.arrival_time = self.agent.ballPred.slices[i].game_seconds simulation = self.aerial.simulate() # # check if we can reach it by an aerial if norm(simulation.location - self.aerial.target) < 100: self.target_ball = self.agent.ballPred.slices[i] self.xOffset = xOffset self.zOffset = zOffset self.active = True break def stillValid(self): for i in range(0, self.agent.ballPred.num_slices): if self.agent.ballPred.slices[i].physics.location.z >= 300: if abs(self.target_ball.game_seconds - self.agent.ballPred.slices[i].game_seconds) < self.agent.deltaTime*3: difference = 0 difference+= abs((self.agent.ballPred.slices[i].physics.location.x+self.xOffset) - (self.target_ball.physics.location.x+self.zOffset)) difference += abs(self.agent.ballPred.slices[i].physics.location.y - self.target_ball.physics.location.y) difference += abs((self.agent.ballPred.slices[i].physics.location.z+self.zOffset) - (self.target_ball.physics.location.z+self.zOffset)) if difference < 10: self.aerial.target = vec3(self.agent.ballPred.slices[i].physics.location.x + self.xOffset, self.agent.ballPred.slices[i].physics.location.y, self.agent.ballPred.slices[i].physics.location.z + self.zOffset) self.aerial.arrival_time = self.agent.ballPred.slices[i].game_seconds self.target_ball = self.agent.ballPred.slices[i] simulation = self.aerial.simulate() return self.active = False self.setup() def update(self): if self.agent.me.boostLevel > 0: self.setup() self.aerial.step(self.agent.deltaTime) self.controls = self.aerial.controls self.controls.jump = True if self.aerial.finished: self.active = False else: self.active = False return self.controls