use super::component_api::ComponentInterface;
use super::players::{ENTITY_OFFSET, SLOT_ENTITY_GET_POSITION};
use super::server::ServerComponent;
use super::types::{UID, Vector3};
use super::vtable::{call_vtable, opaque, slots, virtual_fns};
pub const VEHICLES_COMPONENT_UID: UID = 0x3f1f_62ee_9e22_ab19;
slots! {
SLOT_CREATE_VEHICLE: usize = 19, 18;
SLOT_VEHICLE_SET_COLOUR: usize = 11, 10;
SLOT_VEHICLE_SET_HEALTH: usize = 13, 12;
SLOT_VEHICLE_GET_HEALTH: usize = 14, 13;
SLOT_VEHICLE_GET_MODEL: usize = 57, 56;
SLOT_VEHICLE_DISPATCHER: usize = 21, 19;
VEHICLE_POOL_OFFSET: isize = 44, 64;
}
opaque! {
pub IVehiclesComponent;
pub IVehicle;
pub IVehicleDispatcher;
}
#[cfg(not(target_env = "msvc"))]
#[repr(C)]
pub struct VehicleHandlerVTable {
pub on_vehicle_stream_in: unsafe extern "C" fn(*mut VehicleHandler, *mut IVehicle, *mut u8),
pub on_vehicle_stream_out: unsafe extern "C" fn(*mut VehicleHandler, *mut IVehicle, *mut u8),
pub on_vehicle_death: unsafe extern "C" fn(*mut VehicleHandler, *mut IVehicle, *mut u8),
pub on_player_enter_vehicle:
unsafe extern "C" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, bool),
pub on_player_exit_vehicle: unsafe extern "C" fn(*mut VehicleHandler, *mut u8, *mut IVehicle),
pub on_vehicle_damage_status_update:
unsafe extern "C" fn(*mut VehicleHandler, *mut IVehicle, *mut u8),
pub on_vehicle_paint_job:
unsafe extern "C" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, i32) -> bool,
pub on_vehicle_mod:
unsafe extern "C" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, i32) -> bool,
pub on_vehicle_respray:
unsafe extern "C" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, i32, i32) -> bool,
pub on_enter_exit_mod_shop: unsafe extern "C" fn(*mut VehicleHandler, *mut u8, bool, i32),
pub on_vehicle_spawn: unsafe extern "C" fn(*mut VehicleHandler, *mut IVehicle),
pub on_unoccupied_vehicle_update:
unsafe extern "C" fn(*mut VehicleHandler, *mut IVehicle, *mut u8, *const u8) -> bool,
pub on_trailer_update:
unsafe extern "C" fn(*mut VehicleHandler, *mut u8, *mut IVehicle) -> bool,
pub on_vehicle_siren_state_change:
unsafe extern "C" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, u8) -> bool,
}
#[cfg(target_env = "msvc")]
#[repr(C)]
pub struct VehicleHandlerVTable {
pub on_vehicle_stream_in:
unsafe extern "thiscall" fn(*mut VehicleHandler, *mut IVehicle, *mut u8),
pub on_vehicle_stream_out:
unsafe extern "thiscall" fn(*mut VehicleHandler, *mut IVehicle, *mut u8),
pub on_vehicle_death: unsafe extern "thiscall" fn(*mut VehicleHandler, *mut IVehicle, *mut u8),
pub on_player_enter_vehicle:
unsafe extern "thiscall" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, bool),
pub on_player_exit_vehicle:
unsafe extern "thiscall" fn(*mut VehicleHandler, *mut u8, *mut IVehicle),
pub on_vehicle_damage_status_update:
unsafe extern "thiscall" fn(*mut VehicleHandler, *mut IVehicle, *mut u8),
pub on_vehicle_paint_job:
unsafe extern "thiscall" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, i32) -> bool,
pub on_vehicle_mod:
unsafe extern "thiscall" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, i32) -> bool,
pub on_vehicle_respray:
unsafe extern "thiscall" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, i32, i32) -> bool,
pub on_enter_exit_mod_shop:
unsafe extern "thiscall" fn(*mut VehicleHandler, *mut u8, bool, i32),
pub on_vehicle_spawn: unsafe extern "thiscall" fn(*mut VehicleHandler, *mut IVehicle),
pub on_unoccupied_vehicle_update:
unsafe extern "thiscall" fn(*mut VehicleHandler, *mut IVehicle, *mut u8, *const u8) -> bool,
pub on_trailer_update:
unsafe extern "thiscall" fn(*mut VehicleHandler, *mut u8, *mut IVehicle) -> bool,
pub on_vehicle_siren_state_change:
unsafe extern "thiscall" fn(*mut VehicleHandler, *mut u8, *mut IVehicle, u8) -> bool,
}
#[repr(C)]
pub struct VehicleHandler {
vtable: *const VehicleHandlerVTable,
}
unsafe impl Send for VehicleHandler {}
unsafe impl Sync for VehicleHandler {}
impl VehicleHandler {
#[must_use]
pub fn new(vtable: *const VehicleHandlerVTable) -> Self {
Self { vtable }
}
}
virtual_fns! {
#[must_use]
pub fn vehicle_event_dispatcher(component: IVehiclesComponent) -> *mut IVehicleDispatcher = [0, SLOT_VEHICLE_DISPATCHER] or std::ptr::null_mut();
}
pub unsafe fn add_vehicle_handler(
dispatcher: *mut IVehicleDispatcher,
handler: *mut VehicleHandler,
) -> bool {
call_vtable!(
dispatcher.cast::<u8>(),
0,
0,
(*mut VehicleHandler, i8) -> bool,
(handler, 0),
false
)
}
virtual_fns! {
#[must_use]
pub fn vehicle_by_id(component: IVehiclesComponent, id: i32) -> *mut IVehicle = [VEHICLE_POOL_OFFSET, super::players::SLOT_POOL_GET] or std::ptr::null_mut();
#[must_use]
pub fn vehicle_id(vehicle: IVehicle) -> i32 = [ENTITY_OFFSET, super::players::SLOT_ENTITY_GET_ID] or -1;
}
#[must_use]
#[deprecated(
since = "3.6.0",
note = "the UID and the cast can disagree; use `omp_query::<Component<IVehiclesComponent>>()` and `Component::as_ptr`"
)]
pub unsafe fn as_vehicles_component(component: *mut ServerComponent) -> *mut IVehiclesComponent {
component.cast::<IVehiclesComponent>()
}
#[must_use]
#[allow(clippy::too_many_arguments)]
pub unsafe fn create_vehicle(
component: *mut IVehiclesComponent,
model: i32,
position: Vector3,
z_angle: f32,
colour1: i32,
colour2: i32,
respawn_delay_secs: i64,
siren: bool,
) -> *mut IVehicle {
call_vtable!(
component.cast::<u8>(),
0,
SLOT_CREATE_VEHICLE,
(bool, i32, Vector3, f32, i32, i32, i64, bool) -> *mut IVehicle,
(false, model, position, z_angle, colour1, colour2, respawn_delay_secs, siren),
std::ptr::null_mut()
)
}
virtual_fns! {
#[must_use]
pub fn vehicle_model(vehicle: IVehicle) -> i32 = [0, SLOT_VEHICLE_GET_MODEL] or 0;
#[must_use]
pub fn vehicle_health(vehicle: IVehicle) -> f32 = [0, SLOT_VEHICLE_GET_HEALTH] or 0.0;
pub fn vehicle_set_health(vehicle: IVehicle, health: f32) = [0, SLOT_VEHICLE_SET_HEALTH];
pub fn vehicle_set_colour(vehicle: IVehicle, colour1: i32, colour2: i32) = [0, SLOT_VEHICLE_SET_COLOUR];
#[must_use]
pub fn vehicle_position(vehicle: IVehicle) -> Vector3 = [ENTITY_OFFSET, SLOT_ENTITY_GET_POSITION] or Vector3::ZERO;
}
impl ComponentInterface for IVehiclesComponent {
const UID: UID = VEHICLES_COMPONENT_UID;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn uid_matches_the_header() {
assert_eq!(VEHICLES_COMPONENT_UID, 0x3f1f_62ee_9e22_ab19);
}
#[test]
fn slots_match_what_clang_reports() {
#[cfg(not(target_env = "msvc"))]
let expected = [19, 11, 13, 14, 57];
#[cfg(target_env = "msvc")]
let expected = [18, 10, 12, 13, 56];
assert_eq!(
[
SLOT_CREATE_VEHICLE,
SLOT_VEHICLE_SET_COLOUR,
SLOT_VEHICLE_SET_HEALTH,
SLOT_VEHICLE_GET_HEALTH,
SLOT_VEHICLE_GET_MODEL,
],
expected
);
}
#[test]
fn a_null_component_creates_nothing() {
let position = Vector3::ZERO;
let vehicle =
unsafe { create_vehicle(std::ptr::null_mut(), 411, position, 0.0, -1, -1, -1, false) };
assert!(vehicle.is_null());
}
}