make a new function called timewallexsrap this is an exstraption version that uses logic like this // New reward cutoff logic
float goalY = RLConst::SOCCAR_GOAL_SCORE_BASE_THRESHOLD_Y * (player.team == Team::BLUE ? 1 : -1);
float distToGoalY = std::abs(ball.pos.y - goalY);
float extrapScoreTime = distToGoalY / std::max(std::abs(ball.vel.y), 0.01f);
Vec extrapScorePos = ball.pos + ball.vel * extrapScoreTime;
extrapScorePos.z += CommonValues::GRAVITY_Z * (extrapScoreTime * extrapScoreTime) / 2;
if (extrapScoreTime <= MIN_SCORE_TIME || extrapScorePos.z >= MAX_EXTRAP_Z) {
                reward = 0.0f; // Disable reward if the ball is about to score or go too high
} but just for the car and the walls the nwhen its done finding the closest wall it then renders  to rlbot a line from the car too the point on the wall the exstraption logic thinks is the spot the player is landing on GameState ToGameState(rlbot::GameTickPacket& gameTickPacket, int ticksElapsed, int tickSkip, const std::vector<RocketSim::Car*>& rocketSimCars, uint64_t tickCount, const RLGSC::Action& controls) {
    GameState gs = {};

    const rlbot::flat::Touch* latestTouch = gameTickPacket->ball()->latestTouch();
    float currentTime = gameTickPacket->gameInfo()->secondsElapsed();

    auto players = gameTickPacket->players();
    for (int i = 0; i < players->size(); i++) {
        RocketSim::Car* rocketSimCar = (i < rocketSimCars.size()) ? rocketSimCars[i] : nullptr;
        PlayerData player = ToPlayer(players->Get(i), rocketSimCar, tickCount, tickSkip, gameTickPacket, controls);

        if (latestTouch != nullptr &&
            latestTouch->gameSeconds() > 0 &&
            i == latestTouch->playerIndex() &&
            currentTime - latestTouch->gameSeconds() < tickSkip / 120.0f) {
            player.ballTouchedStep = true;
        }
        else {
            player.ballTouchedStep = false;
        }

        gs.players.push_back(player);
    }

    gs.ball = ToPhysObj(gameTickPacket->ball()->physics());
    gs.ballInv = gs.ball.Invert();

    auto boostPadStates = gameTickPacket->boostPadStates();
    if (boostPadStates->size() != CommonValues::BOOST_LOCATIONS_AMOUNT) {
        if (rand() % 20 == 0) {
            RG_LOG(
                "RLBotClient ToGameState(): Bad boost pad amount, expected " << CommonValues::BOOST_LOCATIONS_AMOUNT << " but got " << boostPadStates->size()
            );
        }
        std::fill(gs.boostPads.begin(), gs.boostPads.end(), 1);
    }
    else {
        for (int i = 0; i < CommonValues::BOOST_LOCATIONS_AMOUNT; i++) {
            gs.boostPads[i] = boostPadStates->Get(i)->isActive();
            gs.boostPadsInv[CommonValues::BOOST_LOCATIONS_AMOUNT - i - 1] = gs.boostPads[i];
            gs.boostPadTimers[i] = boostPadStates->Get(i)->timer();
            gs.boostPadTimersInv[CommonValues::BOOST_LOCATIONS_AMOUNT - i - 1] = gs.boostPadTimers[i];
        }
    }

    return gs;
}

void RLBotBot::RenderBallInfo(const RLGSC::GameState& gs) {
    rlbot::ScopedRenderer renderer("ball_info_renderer");

    float x = 1550.0f;  // Right side of the screen
    float y = 500.0f;   // Moved further down
    float marginLeft = 20.0f;
    float marginTop = 20.0f;
    float nameSpacing = 150.0f;
    float lineSpacing = 25.0f;
    float padding = 20.0f;
    float width = 350.0f;  // Increased width to fit all text
    float extraBottomPadding = 20.0f;

    std::vector<std::pair<std::string, std::string>> lines;

    auto addLine = [&](const std::string& label, const std::string& value) {
        lines.emplace_back(label, value);
        };

    auto formatFloat = [](float value) -> std::string {
        std::ostringstream ss;
        ss << std::fixed << std::setprecision(2) << value;
        return ss.str();
        };

    // Ball position
    addLine("Ball X", formatFloat(gs.ball.pos.x));
    addLine("Ball Y", formatFloat(gs.ball.pos.y));
    addLine("Ball Z", formatFloat(gs.ball.pos.z));

    // Ball velocity in UU
    float velocityUU = gs.ball.vel.Length();
    addLine("Velocity (UU/s)", formatFloat(velocityUU));

    float height = marginTop + (lines.size() + 1) * lineSpacing + padding + extraBottomPadding;
    renderer.DrawRect2D(rlbot::Color{ 64, 64, 64, 192 }, rlbot::flat::Vector3{ x, y, 0 }, width, height, true);

    float currentLine = y + marginTop;
    renderer.DrawString2D("Ball Info", rlbot::Color{ 255, 255, 255, 255 }, rlbot::flat::Vector3{ x + marginLeft, currentLine, 0 }, 1, 1);
    currentLine += lineSpacing * 1.5f;

    for (const auto& line : lines) {
        const auto& [label, value] = line;
        renderer.DrawString2D(label, rlbot::Color{ 255, 255, 255, 255 }, rlbot::flat::Vector3{ x + marginLeft, currentLine, 0 }, 1, 1);
        renderer.DrawString2D(value, rlbot::Color{ 255, 255, 255, 255 }, rlbot::flat::Vector3{ x + marginLeft + nameSpacing, currentLine, 0 }, 1, 1);
        currentLine += lineSpacing;
    }
}

void RLBotBot::RenderCarInfo(const RLGSC::GameState& gs) {
    rlbot::ScopedRenderer renderer("car_info_renderer");

    
    float x = 1550.0f;  // Right side of the screen
    float y = 750.0f;   // Positioned below the ball info panel
    float marginLeft = 20.0f;
    float marginTop = 20.0f;
    float nameSpacing = 150.0f;
    float lineSpacing = 25.0f;
    float padding = 20.0f;
    float width = 350.0f;
    float extraBottomPadding = 20.0f;

    std::vector<std::pair<std::string, std::string>> lines;

    auto addLine = [&](const std::string& label, const std::string& value) {
        lines.emplace_back(label, value);
        };

    auto formatFloat = [](float value) -> std::string {
        std::ostringstream ss;
        ss << std::fixed << std::setprecision(2) << value;
        return ss.str();
        };

    const auto& car = gs.players[index];

    // Car position
    addLine("Car X", formatFloat(car.phys.pos.x));
    addLine("Car Y", formatFloat(car.phys.pos.y));
    addLine("Car Z", formatFloat(car.phys.pos.z));

    // Car velocity in UU
    float velocityUU = car.phys.vel.Length();
    addLine("Velocity (UU/s)", formatFloat(velocityUU));


    // Additional car-specific info
    addLine("Boost", formatFloat(car.boostFraction * 100) + "%");
    addLine("On Ground", car.carState.isOnGround ? "Yes" : "No");
    addLine("Has Flip", car.hasFlip ? "Yes" : "No");

    float height = marginTop + (lines.size() + 1) * lineSpacing + padding + extraBottomPadding;
    renderer.DrawRect2D(rlbot::Color{ 64, 64, 64, 192 }, rlbot::flat::Vector3{ x, y, 0 }, width, height, true);

    float currentLine = y + marginTop;
    renderer.DrawString2D("Car Info", rlbot::Color{ 255, 255, 255, 255 }, rlbot::flat::Vector3{ x + marginLeft, currentLine, 0 }, 1, 1);
    currentLine += lineSpacing * 1.5f;

    for (const auto& line : lines) {
        const auto& [label, value] = line;
        renderer.DrawString2D(label, rlbot::Color{ 255, 255, 255, 255 }, rlbot::flat::Vector3{ x + marginLeft, currentLine, 0 }, 1, 1);
        renderer.DrawString2D(value, rlbot::Color{ 255, 255, 255, 255 }, rlbot::flat::Vector3{ x + marginLeft + nameSpacing, currentLine, 0 }, 1, 1);
        currentLine += lineSpacing;
    }
}

void RLBotBot::RenderReward(const RLGSC::GameState& gs) {
    rlbot::ScopedRenderer renderer("reward_renderer");
    if (!renderRewardEnabled) {
        return;
    }
    float totalReward = 0.0f;
    static thread_local std::vector<float> individualRewards;
    static thread_local int counter = 0;
    counter++;

    if (counter % 8 == 0 || true) {

        individualRewards.clear();
        for (auto& rewardFunction : rewardFunctions) {
            if (rewardFunction == nullptr) {
                individualRewards.push_back(0.0f);
                continue;
            }
            try {
                float reward = rewardFunction->GetReward(gs.players[index], gs, controls);
                totalReward += reward;
                individualRewards.push_back(reward);
            }
            catch (...) {
                individualRewards.push_back(0.0f);
            }
        }
    }

    
    lastReward = totalReward;
    if (totalReward < minReward) minReward = totalReward;
    if (totalReward > maxReward) maxReward = totalReward;
    totalRewards += totalReward;
    numRewards++;
    float avgReward = totalRewards / numRewards;

    // Changed positioning to top-left corner
    float x = 20.0f;
    float y = 20.0f;
    float z = 0.0f;  // Z-axis for 3D space
    float marginLeft = 20.0f;
    float marginTop = 20.0f;
    float nameSpacing = 200.0f;  // Spacing between name and value
    float lineSpacing = 25.0f;
    float padding = 20.0f;
    float width = 350.0f;
    float extraBottomPadding = 20.0f;
    float lineThickness = 2.0f;  // Thickness of the horizontal line
    std::vector<std::pair<std::string, std::string>> lines;
    auto addLine = [&](const std::string& label, float value) {
        lines.emplace_back(label, formatFloat(value));
        };
    auto rewardNames = GetRewardNames();
    for (size_t i = 0; i < rewardFunctions.size() && i < individualRewards.size(); ++i) {
        addLine(rewardNames[i], individualRewards[i]);
    }
    addLine("Total Reward", totalReward);
    addLine("Min", minReward);
    addLine("Max", maxReward);
    addLine("Average", avgReward);
    float height = marginTop + (lines.size() + 1) * lineSpacing + padding + extraBottomPadding;
    renderer.DrawRect2D(rlbot::Color{ 64, 64, 64, 192 }, rlbot::flat::Vector3{ x, y, 0 }, width, height, true);
    float currentLine = y + marginTop;
    renderer.DrawString2D("Rewards", rlbot::Color{ 255, 255, 255, 255 }, rlbot::flat::Vector3{ x + marginLeft, currentLine, 0 }, 1, 1);
    currentLine += lineSpacing * 1.5f;
    for (const auto& line : lines) {
        const auto& [label, value] = line;
        renderer.DrawString2D(label, rlbot::Color{ 255, 255, 255, 255 }, rlbot::flat::Vector3{ x + marginLeft, currentLine, 0 }, 1, 1);
        renderer.DrawString2D(value, rlbot::Color{ 255, 255, 255, 255 }, rlbot::flat::Vector3{ x + marginLeft + nameSpacing, currentLine, 0 }, 1, 1);
        // Adjust line position to be further down between the current text and the next
        float lineX = x + marginLeft;
        float lineY = currentLine + lineSpacing * 0.75f;  // Increase the offset to lower the line further down
        // Draw the horizontal line using DrawRect2D
        renderer.DrawRect2D(
            rlbot::Color{ 255, 255, 255, 128 }, // Line color with some transparency
            rlbot::flat::Vector3{ lineX, lineY, 0 },
            width - 2 * marginLeft,  // Line width
            lineThickness,           // Line height (thickness)
            true                     // Filled rectangle for solid line
        );
        currentLine += lineSpacing;
    }
    rlbot::flat::Vector3 localPlayerLocation = {
        gs.players[index].phys.pos.x,
        gs.players[index].phys.pos.y,
        gs.players[index].phys.pos.z + 150.0f
    };
    std::string currentRewardText = " " + formatFloat(lastReward);
    renderer.DrawString3D(currentRewardText, rlbot::Color{ 255, 255, 255, 255 }, localPlayerLocation, 2, 2);
}

RLGSC::Action ConvertToAction(const rlbot::Controller& controller) {
    RLGSC::Action action;
    action.throttle = controller.throttle;
    action.steer = controller.steer;
    action.pitch = controller.pitch;
    action.yaw = controller.yaw;
    action.roll = controller.roll;
    action.jump = controller.jump;
    action.boost = controller.boost;
    action.handbrake = controller.handbrake;
    return action;
}

void RLBotBot::RenderBallPredictions(const RLGSC::GameState& gs) {
    rlbot::ScopedRenderer renderer("rocketsim_ballpred");

    if (!ballPredTracker) {
        return;
    }

    // Define color variables for conditional transparency
    rlbot::Color visibleColorBlue = rlbot::Color::blue;
    rlbot::Color visibleColorGreen = rlbot::Color::green;
    rlbot::Color transparentColor{ 0, 0, 0, 0 };

    if (!renderBallPredictionsEnabled) {
        visibleColorBlue = transparentColor;
        visibleColorGreen = transparentColor;
    }

    RocketSim::BallState currentBallState;
    currentBallState.pos = RocketSim::Vec(gs.ball.pos.x, gs.ball.pos.y, gs.ball.pos.z);
    currentBallState.vel = RocketSim::Vec(gs.ball.vel.x, gs.ball.vel.y, gs.ball.vel.z);
    currentBallState.angVel = RocketSim::Vec(gs.ball.angVel.x, gs.ball.angVel.y, gs.ball.angVel.z);
    currentBallState.rotMat = RocketSim::RotMat(gs.ball.rotMat.forward, gs.ball.rotMat.right, gs.ball.rotMat.up);
    ballPredTracker->UpdatePredManual(currentBallState, 0);

    std::vector<rlbot::flat::Vector3> points;
    std::vector<const rlbot::flat::Vector3*> pointPointers;
    size_t numPoints = RS_MIN(size_t(240), ballPredTracker->predData.size());
    points.reserve(numPoints);
    pointPointers.reserve(numPoints);

    for (size_t i = 0; i < numPoints; ++i) {
        const auto& slice = ballPredTracker->predData[i];
        points.push_back(rlbot::flat::Vector3{
            static_cast<float>(slice.pos.x),
            static_cast<float>(slice.pos.y),
            static_cast<float>(slice.pos.z)
            });
        pointPointers.push_back(&points.back());
    }

    // Draw the prediction line with conditional color
    renderer.DrawPolyLine3D(visibleColorBlue, pointPointers);

    // Draw a green dot at the end of the prediction line with conditional color
    if (!points.empty()) {
        renderer.DrawRect3D(
            visibleColorGreen,    // Color of the dot
            points.back(),        // Position of the dot
            10,                   // Width of the dot
            10,                   // Height of the dot
            true,                 // Filled
            true                  // Centered
        );
    }
}










void RLBotBot::UpdatePredictionArena(const RLGSC::GameState& gs) {
    if (!updatePredictionArenaEnabled) return;
    if (predictionArena == nullptr) {
        std::cout << "Prediction arena is null" << std::endl;
        return;
    }

    // Update ball state
    RocketSim::BallState ballState = predictionArena->ball->GetState();
    ballState.pos = RocketSim::Vec(gs.ball.pos.x, gs.ball.pos.y, gs.ball.pos.z);
    ballState.vel = RocketSim::Vec(gs.ball.vel.x, gs.ball.vel.y, gs.ball.vel.z);
    ballState.angVel = RocketSim::Vec(gs.ball.angVel.x, gs.ball.angVel.y, gs.ball.angVel.z);
    predictionArena->ball->SetState(ballState);

    auto cars = predictionArena->GetCars();
    if (cars.empty()) {
        std::cout << "No cars in prediction arena" << std::endl;
        return;
    }

    auto localCar = *cars.begin();

    // Update local car state
    const auto& localPlayer = gs.players[index];
    RocketSim::CarState localCarState = localCar->GetState();
    localCarState.pos = RocketSim::Vec(localPlayer.phys.pos.x, localPlayer.phys.pos.y, localPlayer.phys.pos.z);
    localCarState.vel = RocketSim::Vec(localPlayer.phys.vel.x, localPlayer.phys.vel.y, localPlayer.phys.vel.z);
    localCarState.angVel = RocketSim::Vec(localPlayer.phys.angVel.x, localPlayer.phys.angVel.y, localPlayer.phys.angVel.z);
    localCarState.rotMat = localPlayer.phys.rotMat;
    localCarState.isDemoed = localPlayer.carState.isDemoed;
    localCar->SetState(localCarState);

    // Update local car controls
    RocketSim::CarControls localControls;
    localControls.throttle = controls.throttle;
    localControls.steer = controls.steer;
    localControls.pitch = controls.pitch;
    localControls.yaw = controls.yaw;
    localControls.roll = controls.roll;
    localControls.boost = controls.boost;
    localControls.jump = controls.jump;
    localControls.handbrake = controls.handbrake;
    localCar->controls = localControls;

    // Update opponent car if present
    if (cars.size() > 1) {
        auto opponentCar = *(std::next(cars.begin()));
        const PlayerData* opponent = nullptr;
        for (const auto& player : gs.players) {
            if (player.team != localPlayer.team) {
                opponent = &player;
                break;
            }
        }

        if (opponent) {
            RocketSim::CarState opponentCarState = opponentCar->GetState();
            opponentCarState.pos = RocketSim::Vec(opponent->phys.pos.x, opponent->phys.pos.y, opponent->phys.pos.z);
            opponentCarState.vel = RocketSim::Vec(opponent->phys.vel.x, opponent->phys.vel.y, opponent->phys.vel.z);
            opponentCarState.angVel = RocketSim::Vec(opponent->phys.angVel.x, opponent->phys.angVel.y, opponent->phys.angVel.z);
            opponentCarState.rotMat = opponent->phys.rotMat;
            opponentCarState.hasJumped = opponent->carState.hasJumped;
            opponentCarState.hasDoubleJumped = opponent->carState.hasDoubleJumped;
            opponentCarState.isDemoed = opponent->carState.isDemoed;
            opponentCar->SetState(opponentCarState);
        }
        else {
            //std::cout << "Opponent not found in game state" << std::endl;
        }
    }

    predictionArena->Step(1);
}