From 0c6acb758147b9d6eaddeaaafc268e9dfd69d12a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=B5=E8=87=B4=E5=BC=B7?= Date: Thu, 14 Sep 2023 23:19:32 +0800 Subject: [PATCH] feat(Vehicle): Added option to override vehicle weapons. (#2081) --- .../vehicle/vehicle_ammo_special_type.cpp | 248 ++++++++++++++++++ src/core/settings.hpp | 26 +- src/views/vehicle/view_fun_vehicle.cpp | 103 +++++++- 3 files changed, 375 insertions(+), 2 deletions(-) create mode 100644 src/backend/looped/vehicle/vehicle_ammo_special_type.cpp diff --git a/src/backend/looped/vehicle/vehicle_ammo_special_type.cpp b/src/backend/looped/vehicle/vehicle_ammo_special_type.cpp new file mode 100644 index 00000000..26b11b50 --- /dev/null +++ b/src/backend/looped/vehicle/vehicle_ammo_special_type.cpp @@ -0,0 +1,248 @@ +#include "backend/looped_command.hpp" + +namespace big +{ + class custom_vehicle_weapon : looped_command + { + using looped_command::looped_command; + using vehicle_ammo_setting = struct menu_settings::vehicle::vehicle_ammo_special; + using CWeaponInfoFlags = std::bitset<192>; + // mg + CWeaponInfo* m_mg_weapon_info = nullptr; + eDamageType m_mg_damage_type = eDamageType::None; + CWeaponInfo::sExplosion m_mg_explosion{}; + eAmmoSpecialType m_mg_ammo_type = eAmmoSpecialType::None; + float m_mg_speed = 0; + float m_mg_time_between_shots = 0; + float m_mg_alternate_wait_time = 0; + float m_mg_range = 0; + // rocket + CWeaponInfo* m_rocket_weapon_info = nullptr; + eDamageType m_rocket_damage_type = eDamageType::Explosive; + CWeaponInfoFlags m_rocket_weapon_flags = 0; + float m_rocket_time_between_shots = 0; + float m_rocket_alternate_wait_time = 0; + float m_rocket_lock_on_range = 0; + float m_rocket_range = 0; + float m_rocket_reload_time_mp = 0; + float m_rocket_reload_time_sp = 0; + // rocket ammo + CAmmoRocketInfo::sExplosion m_rocket_explosion{}; + float m_rocket_lifetime = 0; + float m_rocket_launch_speed = 0; + float m_rocket_time_before_homing = 0; + CHomingRocketParams m_rocket_homing_params{}; + + virtual void on_tick() override + { + if (g_local_player == nullptr || g_local_player->m_weapon_manager == nullptr + || g_local_player->m_weapon_manager->m_vehicle_weapon_info == nullptr + || g_local_player->m_weapon_manager->m_vehicle_weapon_info->m_ammo_info == nullptr) + { + return; + } + + CWeaponInfo* weapon_info = g_local_player->m_weapon_manager->m_vehicle_weapon_info; + if (is_weapon_mg(weapon_info)) + { + // check if the player changed their weapon + if (m_mg_weapon_info != weapon_info) + { + // apply the original bullet and impact type to the old weapon + restore_mg(); + + // backup the bullet and impact type of the new weapon + backup_mg(weapon_info); + } + + // apply ammo type changes to the current weapon + apply_mg(g.vehicle.vehicle_ammo_special); + } + else if (is_weapon_rocket(weapon_info)) + { + if (m_rocket_weapon_info != weapon_info) + { + restore_rocket(); + backup_rocket(weapon_info); + } + apply_rocket(g.vehicle.vehicle_ammo_special); + } + } + + virtual void on_disable() override + { + restore_mg(); + restore_rocket(); + } + + CWeaponInfoFlags& weapon_flags(CWeaponInfo* weapon_info) + { + return *((CWeaponInfoFlags*)((char*)weapon_info + 0x900)); + } + + bool is_weapon_mg(const CWeaponInfo* weapon_info) + { + return weapon_info->m_fire_type == eFireType::InstantHit || weapon_info->m_fire_type == eFireType::DelayedHit; + } + + bool is_weapon_rocket(const CWeaponInfo* weapon_info) + { + if (weapon_info->m_fire_type != eFireType::ProjectTile) + return false; + // this is to differentiate missiles from turrets, mortars and barrage + CAmmoRocketInfo* rocket_info = (CAmmoRocketInfo*)weapon_info->m_ammo_info; + uint32_t trail_hash = rocket_info->m_m_trail_fx_hash; + return (trail_hash == RAGE_JOAAT("proj_rpg_trail") || trail_hash == RAGE_JOAAT("proj_xm_thruster_rpg_trail")); + } + + void restore_mg() + { + // apply the original bullet and impact type to the old weapon + if (m_mg_weapon_info != nullptr) + { + m_mg_weapon_info->m_damage_type = m_mg_damage_type; + m_mg_weapon_info->m_explosion = m_mg_explosion; + m_mg_weapon_info->m_ammo_info->m_ammo_special_type = m_mg_ammo_type; + m_mg_weapon_info->m_speed = m_mg_speed; + m_mg_weapon_info->m_time_between_shots = m_mg_time_between_shots; + m_mg_weapon_info->m_alternate_wait_time = m_mg_alternate_wait_time; + m_mg_weapon_info->m_weapon_range = m_mg_range; + } + } + + void backup_mg(CWeaponInfo* weapon_info) + { + // backup the bullet and impact type of the new weapon + m_mg_weapon_info = weapon_info; + m_mg_damage_type = weapon_info->m_damage_type; + m_mg_explosion = weapon_info->m_explosion; + m_mg_ammo_type = weapon_info->m_ammo_info->m_ammo_special_type; + m_mg_speed = weapon_info->m_speed; + m_mg_time_between_shots = weapon_info->m_time_between_shots; + m_mg_alternate_wait_time = weapon_info->m_alternate_wait_time; + m_mg_range = weapon_info->m_weapon_range; + } + + void apply_mg(const vehicle_ammo_setting& g_vehicle_ammo_settings) + { + // apply ammo type changes to the current weapon + eDamageType damage_type = eDamageType::None; + eExplosionTag explosion_tag = g_vehicle_ammo_settings.explosion_tag; + eAmmoSpecialType ammo_type = eAmmoSpecialType::None; + + if (explosion_tag == eExplosionTag::DONTCARE) + { + damage_type = m_mg_damage_type; + ammo_type = g_vehicle_ammo_settings.type; + } + else + { + damage_type = eDamageType::Explosive; + ammo_type = m_mg_ammo_type; + } + + m_mg_weapon_info->m_damage_type = damage_type; + + CWeaponInfo::sExplosion explosion; + explosion.m_default = explosion_tag; + explosion.m_hit_bike = explosion_tag; + explosion.m_hit_boat = explosion_tag; + explosion.m_hit_car = explosion_tag; + explosion.m_hit_plane = explosion_tag; + explosion.m_hit_truck = explosion_tag; + m_mg_weapon_info->m_explosion = explosion; + m_mg_weapon_info->m_ammo_info->m_ammo_special_type = ammo_type; + m_mg_weapon_info->m_speed = g_vehicle_ammo_settings.speed; + m_mg_weapon_info->m_time_between_shots = g_vehicle_ammo_settings.time_between_shots; + m_mg_weapon_info->m_alternate_wait_time = g_vehicle_ammo_settings.alternate_wait_time; + m_mg_weapon_info->m_weapon_range = g_vehicle_ammo_settings.weapon_range; + } + + void restore_rocket() + { + if (m_rocket_weapon_info != nullptr) + { + m_rocket_weapon_info->m_damage_type = m_rocket_damage_type; + m_rocket_weapon_info->m_time_between_shots = m_rocket_time_between_shots; + m_rocket_weapon_info->m_alternate_wait_time = m_rocket_alternate_wait_time; + m_rocket_weapon_info->m_lock_on_range = m_rocket_lock_on_range; + m_rocket_weapon_info->m_weapon_range = m_rocket_range; + m_rocket_weapon_info->m_reload_time_mp = m_rocket_reload_time_mp; + m_rocket_weapon_info->m_reload_time_sp = m_rocket_reload_time_sp; + weapon_flags(m_rocket_weapon_info) = m_rocket_weapon_flags; + + CAmmoRocketInfo* rocket_info = (CAmmoRocketInfo*)m_rocket_weapon_info->m_ammo_info; + rocket_info->m_explosion = m_rocket_explosion; + rocket_info->m_lifetime = m_rocket_lifetime; + rocket_info->m_launch_speed = m_rocket_launch_speed; + rocket_info->m_time_before_homing = m_rocket_time_before_homing; + rocket_info->m_homing_rocket_params = m_rocket_homing_params; + m_rocket_weapon_info = nullptr; + } + } + + void backup_rocket(CWeaponInfo* weapon_info) + { + m_rocket_weapon_info = weapon_info; + m_rocket_time_between_shots = weapon_info->m_time_between_shots; + m_rocket_alternate_wait_time = weapon_info->m_alternate_wait_time; + m_rocket_lock_on_range = weapon_info->m_lock_on_range; + m_rocket_range = weapon_info->m_weapon_range; + m_rocket_reload_time_mp = weapon_info->m_reload_time_mp; + m_rocket_reload_time_sp = weapon_info->m_reload_time_sp; + m_rocket_weapon_flags = weapon_flags(weapon_info); + + CAmmoRocketInfo* rocket_info = (CAmmoRocketInfo*)weapon_info->m_ammo_info; + m_rocket_explosion = rocket_info->m_explosion; + m_rocket_lifetime = rocket_info->m_lifetime; + m_rocket_launch_speed = rocket_info->m_launch_speed; + m_rocket_time_before_homing = rocket_info->m_time_before_homing; + m_rocket_homing_params = rocket_info->m_homing_rocket_params; + } + + void apply_rocket(const vehicle_ammo_setting& g_vehicle_ammo_settings) + { + m_rocket_weapon_info->m_damage_type = eDamageType::Explosive; + m_rocket_weapon_info->m_time_between_shots = g_vehicle_ammo_settings.rocket_time_between_shots; + m_rocket_weapon_info->m_alternate_wait_time = g_vehicle_ammo_settings.rocket_alternate_wait_time; + m_rocket_weapon_info->m_weapon_range = g_vehicle_ammo_settings.rocket_range; + m_rocket_weapon_info->m_lock_on_range = g_vehicle_ammo_settings.rocket_lock_on_range; + m_rocket_weapon_info->m_reload_time_mp = g_vehicle_ammo_settings.rocket_reload_time; + m_rocket_weapon_info->m_reload_time_sp = g_vehicle_ammo_settings.rocket_reload_time; + + CAmmoRocketInfo* rocket_info = (CAmmoRocketInfo*)m_rocket_weapon_info->m_ammo_info; + eExplosionTag explosion_tag = g_vehicle_ammo_settings.rocket_explosion_tag; + + CAmmoRocketInfo::sExplosion explosion; + explosion.m_default = explosion_tag; + explosion.m_hit_bike = explosion_tag; + explosion.m_hit_boat = explosion_tag; + explosion.m_hit_car = explosion_tag; + explosion.m_hit_plane = explosion_tag; + explosion.m_hit_truck = explosion_tag; + + rocket_info->m_explosion = explosion; + rocket_info->m_lifetime = g_vehicle_ammo_settings.rocket_lifetime; + rocket_info->m_launch_speed = g_vehicle_ammo_settings.rocket_launch_speed; + rocket_info->m_time_before_homing = g_vehicle_ammo_settings.rocket_time_before_homing; + + if (g_vehicle_ammo_settings.rocket_improve_tracking) + { + rocket_info->m_homing_rocket_params.m_should_use_homing_params_from_info = true; + rocket_info->m_homing_rocket_params.m_turn_rate_modifier = 4.0; + rocket_info->m_homing_rocket_params.m_pitch_yaw_roll_clamp = 8.5; + rocket_info->m_homing_rocket_params.m_default_homing_rocket_break_lock_angle = 0.2; + rocket_info->m_homing_rocket_params.m_default_homing_rocket_break_lock_angle_close = 0.6; + rocket_info->m_homing_rocket_params.m_default_homing_rocket_break_lock_close_distance = 20.0; + rocket_info->m_homing_rocket_params.m_time_before_starting_homing = 0.15; + weapon_flags(m_rocket_weapon_info)[152] = 1; + } + else + { + rocket_info->m_homing_rocket_params = m_rocket_homing_params; + weapon_flags(m_rocket_weapon_info) = m_rocket_weapon_flags; + } + } + }; + custom_vehicle_weapon g_custom_vehicle_weapon("customvehweaps", "CUSTOM_VEH_WEAPONS", "CUSTOM_VEH_WEAPONS_DESC", g.vehicle.vehicle_ammo_special.enabled); +} diff --git a/src/core/settings.hpp b/src/core/settings.hpp index 3a8b39f9..8680e83a 100644 --- a/src/core/settings.hpp +++ b/src/core/settings.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #define IMGUI_DEFINE_MATH_OPERATORS #include @@ -717,7 +718,30 @@ namespace big bool siren_mute = false; bool all_vehs_in_heists = false; - NLOHMANN_DEFINE_TYPE_INTRUSIVE(vehicle, speedo_meter, fly, rainbow_paint, speed_unit, god_mode, proof_bullet, proof_fire, proof_collision, proof_melee, proof_explosion, proof_steam, proof_water, proof_mask, auto_drive_destination, auto_drive_style, auto_drive_speed, auto_turn_signals, boost_behavior, drive_on_water, horn_boost, instant_brake, block_homing, seatbelt, turn_signals, vehicle_jump, keep_vehicle_repaired, no_water_collision, disable_engine_auto_start, change_engine_state_immediately, keep_engine_running, keep_vehicle_clean, vehinvisibility, localveh_visibility, keep_on_ground, no_collision, unlimited_weapons, siren_mute, all_vehs_in_heists) + struct vehicle_ammo_special + { + bool enabled = false; + eAmmoSpecialType type = eAmmoSpecialType::None; + eExplosionTag explosion_tag = eExplosionTag::EXP_TAG_ROGUE_CANNON; + float speed = 2000; + float time_between_shots = 0.04; + float alternate_wait_time = -1; + float weapon_range = 250; + float rocket_time_between_shots = 0.66; + float rocket_alternate_wait_time = 0.66; + float rocket_lock_on_range = 500; + float rocket_range = 1000; + float rocket_reload_time = -1; + eExplosionTag rocket_explosion_tag = eExplosionTag::TANKSHELL; + float rocket_lifetime = 15; + float rocket_launch_speed = 1200; + float rocket_time_before_homing = 0.75; + bool rocket_improve_tracking = true; + + NLOHMANN_DEFINE_TYPE_INTRUSIVE(vehicle_ammo_special, enabled, type, explosion_tag, speed, time_between_shots, alternate_wait_time, weapon_range, rocket_time_between_shots, rocket_alternate_wait_time, rocket_lock_on_range, rocket_range, rocket_reload_time, rocket_explosion_tag, rocket_lifetime, rocket_launch_speed, rocket_time_before_homing, rocket_improve_tracking) + } vehicle_ammo_special{}; + + NLOHMANN_DEFINE_TYPE_INTRUSIVE(vehicle, speedo_meter, fly, rainbow_paint, speed_unit, god_mode, proof_bullet, proof_fire, proof_collision, proof_melee, proof_explosion, proof_steam, proof_water, proof_mask, auto_drive_destination, auto_drive_style, auto_drive_speed, auto_turn_signals, boost_behavior, drive_on_water, horn_boost, instant_brake, block_homing, seatbelt, turn_signals, vehicle_jump, keep_vehicle_repaired, no_water_collision, disable_engine_auto_start, change_engine_state_immediately, keep_engine_running, keep_vehicle_clean, vehinvisibility, localveh_visibility, keep_on_ground, no_collision, unlimited_weapons, siren_mute, all_vehs_in_heists, vehicle_ammo_special) } vehicle{}; struct weapons diff --git a/src/views/vehicle/view_fun_vehicle.cpp b/src/views/vehicle/view_fun_vehicle.cpp index 173f3075..5c904245 100644 --- a/src/views/vehicle/view_fun_vehicle.cpp +++ b/src/views/vehicle/view_fun_vehicle.cpp @@ -1,3 +1,5 @@ +#include "core/data/bullet_impact_types.hpp" +#include "core/data/special_ammo_types.hpp" #include "core/data/speed_units.hpp" #include "core/enums.hpp" #include "fiber_pool.hpp" @@ -144,7 +146,7 @@ namespace big { ImGui::Text("KEEP_VEHICLE_CLEAN"_T.data()); } - else if (g.vehicle.keep_vehicle_repaired) + else if (g.vehicle.keep_vehicle_repaired) { ImGui::Text("KEEP_VEHICLE_REPAIRED"_T.data()); } @@ -246,5 +248,104 @@ namespace big g.vehicle.fly.speed = vehicle::speed_to_mps(fly_speed_user_unit, g.vehicle.speed_unit); } } + ImGui::SeparatorText("CUSTOM_VEH_WEAPONS"_T.data()); + { + components::command_checkbox<"customvehweaps">(std::format("{}##customvehweaps", "ENABLED"_T)); + components::options_modal("CUSTOM_VEH_WEAPONS"_T.data(), [] { + eAmmoSpecialType selected_ammo = g.vehicle.vehicle_ammo_special.type; + eExplosionTag selected_explosion = g.vehicle.vehicle_ammo_special.explosion_tag; + eExplosionTag selected_rocket_explosion = g.vehicle.vehicle_ammo_special.rocket_explosion_tag; + + ImGui::BeginGroup(); + components::sub_title("CUSTOM_VEH_WEAPONS_MG"_T.data()); + if (ImGui::BeginCombo("SPECIAL_AMMO"_T.data(), SPECIAL_AMMOS[(int)selected_ammo].name)) + { + for (const auto& special_ammo : SPECIAL_AMMOS) + { + if (ImGui::Selectable(special_ammo.name, special_ammo.type == selected_ammo)) + { + g.vehicle.vehicle_ammo_special.type = special_ammo.type; + } + + if (special_ammo.type == selected_ammo) + { + ImGui::SetItemDefaultFocus(); + } + } + ImGui::EndCombo(); + } + if (ImGui::BeginCombo("BULLET_IMPACT"_T.data(), BULLET_IMPACTS[selected_explosion])) + { + for (const auto& [type, name] : BULLET_IMPACTS) + { + if (ImGui::Selectable(name, type == selected_explosion)) + { + g.vehicle.vehicle_ammo_special.explosion_tag = type; + } + + if (type == selected_explosion) + { + ImGui::SetItemDefaultFocus(); + } + } + + ImGui::EndCombo(); + } + + ImGui::InputFloat("CUSTOM_VEH_WEAPONS_SPEED"_T.data(), &g.vehicle.vehicle_ammo_special.speed, 10, 100, "%.1f"); + ImGui::InputFloat("CUSTOM_VEH_WEAPONS_RANGE"_T.data(), &g.vehicle.vehicle_ammo_special.weapon_range, 50, 100, "%.1f"); + ImGui::InputFloat("CUSTOM_VEH_WEAPONS_TBS"_T.data(), &g.vehicle.vehicle_ammo_special.time_between_shots, 0.001, 0.1, "%.3f"); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("CUSTOM_VEH_WEAPONS_TBS_DESC"_T.data()); + ImGui::InputFloat("CUSTOM_VEH_WEAPONS_AWT"_T.data(), &g.vehicle.vehicle_ammo_special.alternate_wait_time, 0.001, 0.1, "%.3f"); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("CUSTOM_VEH_WEAPONS_AWT_DESC"_T.data()); + ImGui::EndGroup(); + + ImGui::SameLine(); + ImGui::BeginGroup(); + components::sub_title("CUSTOM_VEH_WEAPONS_MISSILE"_T.data()); + if (ImGui::BeginCombo(std::format("{}##customvehweaps", "EXPLOSION"_T).data(), BULLET_IMPACTS[selected_rocket_explosion])) + { + for (const auto& [type, name] : BULLET_IMPACTS) + { + if (ImGui::Selectable(name, type == selected_rocket_explosion)) + { + g.vehicle.vehicle_ammo_special.rocket_explosion_tag = type; + } + + if (type == selected_rocket_explosion) + { + ImGui::SetItemDefaultFocus(); + } + } + + ImGui::EndCombo(); + } + + ImGui::InputFloat("CUSTOM_VEH_WEAPONS_RELOAD_TIME"_T.data(), &g.vehicle.vehicle_ammo_special.rocket_reload_time, 0.1, 1, "%.1f"); + ImGui::InputFloat(std::format("{}##rocket", "CUSTOM_VEH_WEAPONS_SPEED"_T).data(), + &g.vehicle.vehicle_ammo_special.rocket_launch_speed, 10, 100, "%.1f"); + ImGui::InputFloat(std::format("{}##rocket", "CUSTOM_VEH_WEAPONS_RANGE"_T).data(), + &g.vehicle.vehicle_ammo_special.rocket_range, 50, 100, "%.1f"); + ImGui::InputFloat("CUSTOM_VEH_WEAPONS_LOCKON_RANGE"_T.data(), &g.vehicle.vehicle_ammo_special.rocket_lock_on_range, 50, 100, "%.1f"); + ImGui::InputFloat("CUSTOM_VEH_WEAPONS_LOCKON_TIME"_T.data(), &g.vehicle.vehicle_ammo_special.rocket_time_before_homing, 0.01, 0.1, "%.2f"); + ImGui::InputFloat(std::format("{}##rocket", "CUSTOM_VEH_WEAPONS_TBS"_T).data(), + &g.vehicle.vehicle_ammo_special.rocket_time_between_shots, 0.001, 0.1, "%.3f"); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("CUSTOM_VEH_WEAPONS_TBS_DESC"_T.data()); + ImGui::InputFloat(std::format("{}##rocket", "CUSTOM_VEH_WEAPONS_AWT"_T).data(), + &g.vehicle.vehicle_ammo_special.rocket_alternate_wait_time, 0.001, 0.1, "%.3f"); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("CUSTOM_VEH_WEAPONS_AWT_DESC"_T.data()); + ImGui::InputFloat("CUSTOM_VEH_WEAPONS_LIFETIME"_T.data(), &g.vehicle.vehicle_ammo_special.rocket_lifetime, 0.1, 1, "%.1f"); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("CUSTOM_VEH_WEAPONS_LIFETIME_DESC"_T.data()); + ImGui::Checkbox("CUSTOM_VEH_WEAPONS_SMART_MISSILE"_T.data(), &g.vehicle.vehicle_ammo_special.rocket_improve_tracking); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("CUSTOM_VEH_WEAPONS_SMART_MISSILE_DESC"_T.data()); + ImGui::EndGroup(); + }); + } } }