#include "backend/looped_command.hpp" #include "gta/enums.hpp" #include "util/misc.hpp" #include "util/vehicle.hpp" #include namespace big { class vehicle_ability : looped_command { using looped_command::looped_command; class vehicle_ability_helper { private: static constexpr std::array m_abilities = { eVehicleAbilities::JUMP, eVehicleAbilities::ROCKET, eVehicleAbilities::PARACHUTE, eVehicleAbilities::RAMP_BUGGY, }; private: std::unordered_set m_stock_abilities; CVehicle* m_vehicle = nullptr; uint16_t m_original_abilities; public: vehicle_ability_helper() = default; vehicle_ability_helper(CVehicle* vehicle) { if (vehicle == nullptr || vehicle->m_model_info == nullptr) return; m_vehicle = vehicle; const auto model_info = reinterpret_cast(vehicle->m_model_info); m_original_abilities = model_info->m_ability_flag; for (const auto ability : m_abilities) { if (m_original_abilities >> (uint16_t)ability & 1) { m_stock_abilities.insert(ability); } } } /** * @brief Gets the initial ability value of the vehicle. * * @param ability * @return true * @return false */ bool get_ability_default(eVehicleAbilities ability) { return m_stock_abilities.contains(ability); } /** * @brief Checks if the currently bound CVehicle ptr has the flag enabled. * * @param ability * @return true * @return false */ bool has_ability(eVehicleAbilities ability) { return m_vehicle && m_vehicle->m_model_info && reinterpret_cast(m_vehicle->m_model_info)->m_ability_flag >> (uint16_t)ability & 1; } /** * @brief Resets the vehicle to its defaults safely! */ void reset_defaults() { if (m_vehicle && m_vehicle->m_model_info) { const auto model_info = reinterpret_cast(m_vehicle->m_model_info); model_info->m_ability_flag = m_original_abilities; } } /** * @brief Safely set the ability state of a vehicle. * * @param ability * @param toggle */ void toggle_ability(eVehicleAbilities ability, bool toggle) { if (m_vehicle && m_vehicle->m_model_info) { const auto model_info = reinterpret_cast(m_vehicle->m_model_info); if (toggle) misc::set_bits(&model_info->m_ability_flag, (uint16_t)ability); else misc::clear_bits(&model_info->m_ability_flag, (uint16_t)ability); } } bool operator ==(const CVehicle* vehicle) const { return m_vehicle == vehicle; } } m_vehicle_ability_helper {}; virtual void on_enable() override { reset(g_local_player->m_vehicle); } virtual void on_tick() override { const auto curr_veh = g_local_player->m_vehicle; if (curr_veh && !(g_local_player->m_ped_task_flag & (int)ePedTask::TASK_DRIVING)) { m_vehicle_ability_helper.reset_defaults(); return; } if (m_vehicle_ability_helper != curr_veh) { reset(curr_veh); } m_vehicle_ability_helper.toggle_ability(eVehicleAbilities::JUMP, g.vehicle.abilities.jump); m_vehicle_ability_helper.toggle_ability(eVehicleAbilities::ROCKET, g.vehicle.abilities.rocket); m_vehicle_ability_helper.toggle_ability(eVehicleAbilities::PARACHUTE, g.vehicle.abilities.parachute); m_vehicle_ability_helper.toggle_ability(eVehicleAbilities::RAMP_BUGGY, g.vehicle.abilities.ramp); } virtual void on_disable() override { reset(nullptr); } void reset(CVehicle* vehicle) { if (vehicle == nullptr) { m_vehicle_ability_helper = {}; g.vehicle.abilities = {}; return; } m_vehicle_ability_helper.reset_defaults(); m_vehicle_ability_helper = { vehicle }; // currently I'd keep overwriting the display values for what is default for the current vehicle // should we always persist the user's choice onto the vehicle? or only the ones that are enabled? // doesn't sound like too great of an idea for vehicles that have abilities by default and then suddenly they're disabled g.vehicle.abilities.jump = m_vehicle_ability_helper.get_ability_default(eVehicleAbilities::JUMP); g.vehicle.abilities.rocket = m_vehicle_ability_helper.get_ability_default(eVehicleAbilities::ROCKET); g.vehicle.abilities.parachute = m_vehicle_ability_helper.get_ability_default(eVehicleAbilities::PARACHUTE); g.vehicle.abilities.ramp = m_vehicle_ability_helper.get_ability_default(eVehicleAbilities::RAMP_BUGGY); } }; vehicle_ability g_vehicle_ability("modifyvehicleability", "MODIFY_VEHICLE_ABILITY", "MODIFY_VEHICLE_ABILITY_DESC", g.vehicle.abilities.enabled); bool_command g_jump_ability("jumpability", "BACKEND_LOOPED_VEHICLE_ABILITY_JUMP", "BACKEND_LOOPED_VEHICLE_ABILITY_JUMP_DESC", g.vehicle.abilities.jump); bool_command g_rocket_ability("rocketability", "BACKEND_LOOPED_VEHICLE_ABILITY_ROCKET", "BACKEND_LOOPED_VEHICLE_ABILITY_ROCKET_DESC", g.vehicle.abilities.rocket); bool_command g_parachute_ability("parachuteability", "BACKEND_LOOPED_VEHICLE_ABILITY_PARACHUTE", "BACKEND_LOOPED_VEHICLE_ABILITY_PARACHUTE_DESC", g.vehicle.abilities.parachute); bool_command g_ramp_ability("rampability", "BACKEND_LOOPED_VEHICLE_ABILITY_RAMP", "BACKEND_LOOPED_VEHICLE_ABILITY_RAMP_DESC", g.vehicle.abilities.ramp); }