Files
gtav-src/script/dev_ng/singleplayer/include/public/remote_sniper_public.sch
T
2025-09-29 00:52:08 +02:00

1018 lines
37 KiB
Scheme
Executable File

USING "commands_audio.sch"
USING "commands_camera.sch"
USING "commands_clock.sch"
USING "commands_debug.sch"
USING "commands_fire.sch"
USING "commands_entity.sch"
USING "commands_graphics.sch"
USING "commands_hud.sch"
USING "commands_misc.sch"
USING "commands_object.sch"
USING "commands_pad.sch"
USING "commands_ped.sch"
USING "commands_player.sch"
USING "commands_script.sch"
USING "commands_streaming.sch"
USING "commands_task.sch"
USING "commands_vehicle.sch"
USING "commands_interiors.sch"
USING "model_enums.sch"
USING "script_player.sch"
USING "script_misc.sch"
#IF IS_DEBUG_BUILD
USING "shared_debug.sch"
USING "script_debug.sch"
#ENDIF
STRUCT REMOTE_SNIPER_DATA
BOOL b_is_sniping
//BOOL b_is_thermal_active
BOOL b_bullet_impact_this_frame
BOOL b_hud_requested
BOOL b_allow_quit
BOOL b_audio_requested
BOOL b_registered_shot
BOOL b_fire_shot
BOOL b_lock_rotation
BOOL b_animPostFX_Started
INT i_damage
//INT i_sniper_flicker_timer
INT i_sniper_shot_delay
INT i_sniper_shot_timer
INT i_register_time
INT i_zoom_sound_id
INT i_move_sound_id
//FLOAT f_sniper_scanlines_offset
FLOAT f_current_sniper_fov
FLOAT f_current_fov_plus_recoil
//FLOAT f_base_wind_level
//FLOAT f_adjusted_wind_level
//FLOAT f_randomised_wind_level
FLOAT f_wind_offset
FLOAT f_desired_wind_offset
FLOAT f_wind_offset_speed
FLOAT f_sniper_wind_angle
FLOAT f_shot_distance
FLOAT f_rotate_speed
FLOAT f_scale_rotate_speed_zoom
FLOAT f_lock_rotation_min
FLOAT f_lock_rotation_max
FLOAT f_lock_pitch_min
FLOAT f_lock_pitch_max
FLOAT f_max_zoom
VECTOR v_last_shot_dir
VECTOR v_last_shot_rot
VECTOR v_last_impact_pos
//VECTOR v_sniper_future_rot
WEAPON_TYPE use_weapon_type
CAMERA_INDEX cam_rifle
//CAMERA_INDEX cam_recoil
OBJECT_INDEX obj_sniper
SCALEFORM_INDEX sf_hud
ENDSTRUCT
CONST_INT HUD_R 55
CONST_INT HUD_G 235
CONST_INT HUD_B 55
CONST_INT HUD_A 128
CONST_INT DEFAULT_DAMAGE 100
CONST_FLOAT DEFAULT_ROTATE_SPEED 20.0
CONST_FLOAT DEFAULT_SCALE_ROTATE_SPEED_ZOOM 10.0
CONST_FLOAT MAX_ZOOM 1.0
CONST_FLOAT MIN_ZOOM 45.0
CONST_FLOAT MAX_WIND 0.2
PROC SWAP_VALUES(FLOAT &x, FLOAT &y)
FLOAT temp = x
x = y
y = temp
ENDPROC
/// PURPOSE:
/// Converges a value towards a given destination, by adding/removing a given amount.
PROC CONVERGE_VALUE(FLOAT &val, FLOAT desired_val, FLOAT amount_to_converge)
IF val != desired_val
FLOAT converge_amount_this_frame = (amount_to_converge * 30.0) * GET_FRAME_TIME()
IF val - desired_val > converge_amount_this_frame
val -= converge_amount_this_frame
ELIF val - desired_val < -converge_amount_this_frame
val += converge_amount_this_frame
ELSE
val = desired_val
ENDIF
ENDIF
ENDPROC
FUNC FLOAT DISTANCE_BETWEEN_COORDS(VECTOR v1, VECTOR v2, BOOL in_2d = FALSE, BOOL use_square_root = TRUE)
IF in_2d
v1.z = 0.0
v2.z = 0.0
ENDIF
IF use_square_root
RETURN VDIST(v1, v2)
ELSE
RETURN VDIST2(v1, v2)
ENDIF
ENDFUNC
/// PURPOSE:
/// Converts a rotation vector into a normalised direction vector (basically the direction an entity would move if it moved forward at the current rotation).
/// For example: a rotation of <<0.0, 0.0, 45.0>> gives a direction vector of <<-0.707107, 0.707107, 0.0>>
/// PARAMS:
/// v_rot - The rotation vector
/// RETURNS:
/// The direction vector
FUNC VECTOR CONVERT_ROTATION_TO_DIRECTION_VECTOR(VECTOR v_rot)
RETURN <<-SIN(v_rot.z) * COS(v_rot.x), COS(v_rot.z) * COS(v_rot.x), SIN(v_rot.x)>>
ENDFUNC
// commands for optional params (added for FBI2 but might be useful elsewhere)
PROC SET_REMOTE_SNIPER_WEAPON_TYPE(REMOTE_SNIPER_DATA &sniper_data, WEAPON_TYPE use_weapon_type)
sniper_data.use_weapon_type = use_weapon_type
ENDPROC
PROC SET_REMOTE_SNIPER_DAMAGE(REMOTE_SNIPER_DATA &sniper_data, INT i_damage)
sniper_data.i_damage = i_damage
ENDPROC
PROC SET_REMOTE_SNIPER_ROTATE_SPEED(REMOTE_SNIPER_DATA &sniper_data, FLOAT f_rotate_speed)
sniper_data.f_rotate_speed = f_rotate_speed
ENDPROC
PROC SET_REMOTE_SNIPER_SCALE_ROTATE_SPEED_ZOOM(REMOTE_SNIPER_DATA &sniper_data, FLOAT f_scale_rotate_speed_zoom)
sniper_data.f_scale_rotate_speed_zoom = f_scale_rotate_speed_zoom
ENDPROC
PROC SET_REMOTE_SNIPER_SHOT_DISTANCE(REMOTE_SNIPER_DATA &sniper_data, FLOAT f_shot_distance)
sniper_data.f_shot_distance = f_shot_distance
ENDPROC
PROC SET_REMOTE_SNIPER_ALLOW_QUIT(REMOTE_SNIPER_DATA &sniper_data, BOOL b_allow_quit)
sniper_data.b_allow_quit = b_allow_quit
ENDPROC
PROC LOCK_REMOTE_SNIPER_ROTATION(REMOTE_SNIPER_DATA &sniper_data, BOOL b_lock, FLOAT f_min_rot = -360.0, FLOAT f_max_rot = 360.0)
sniper_data.b_lock_rotation = b_lock
sniper_data.f_lock_rotation_min = f_min_rot
sniper_data.f_lock_rotation_max = f_max_rot
ENDPROC
PROC LOCK_REMOTE_SNIPER_PITCH(REMOTE_SNIPER_DATA &sniper_data, FLOAT f_min_pitch = -20.0, FLOAT f_max_pitch = 45.0)
sniper_data.f_lock_pitch_min = f_min_pitch
sniper_data.f_lock_pitch_max = f_max_pitch
ENDPROC
PROC REQUEST_REMOTE_SNIPER_ASSETS(REMOTE_SNIPER_DATA &sniper_data)
REQUEST_MODEL(PROP_POOL_BALL_01)
IF NOT sniper_data.b_hud_requested
sniper_data.sf_hud = REQUEST_SCALEFORM_MOVIE("remote_sniper_hud")
sniper_data.b_hud_requested = TRUE
ENDIF
ENDPROC
FUNC STRING GET_REMOTE_SNIPER_AUDIO_BANK_NAME()
RETURN "Remote_Gun"
ENDFUNC
FUNC BOOL HAVE_REMOTE_SNIPER_ASSETS_LOADED(REMOTE_SNIPER_DATA &sniper_data)
IF HAS_MODEL_LOADED(PROP_POOL_BALL_01)
AND REQUEST_SCRIPT_AUDIO_BANK(GET_REMOTE_SNIPER_AUDIO_BANK_NAME())
IF HAS_SCALEFORM_MOVIE_LOADED(sniper_data.sf_hud)
RETURN TRUE
ENDIF
ENDIF
sniper_data.b_audio_requested = TRUE
RETURN FALSE
ENDFUNC
PROC CREATE_REMOTE_SNIPER(REMOTE_SNIPER_DATA &sniper_data, VECTOR v_start_pos, FLOAT f_start_heading)
IF HAVE_REMOTE_SNIPER_ASSETS_LOADED(sniper_data)
sniper_data.obj_sniper = CREATE_OBJECT_NO_OFFSET(PROP_POOL_BALL_01, v_start_pos)
SET_ENTITY_ROTATION(sniper_data.obj_sniper, <<0.0, 0.0, f_start_heading>>)
FREEZE_ENTITY_POSITION(sniper_data.obj_sniper, TRUE)
SET_ENTITY_COLLISION(sniper_data.obj_sniper, FALSE)
SET_ENTITY_VISIBLE(sniper_data.obj_sniper, FALSE)
SET_MODEL_AS_NO_LONGER_NEEDED(PROP_POOL_BALL_01)
//sniper_data.cam_recoil = CREATE_CAM_WITH_PARAMS("DEFAULT_SCRIPTED_CAMERA", v_start_pos, <<0.0, 0.0, 0.0>>, 45.0, TRUE)
sniper_data.cam_rifle = CREATE_CAM("DEFAULT_SCRIPTED_CAMERA", TRUE)
ATTACH_CAM_TO_ENTITY(sniper_data.cam_rifle, sniper_data.obj_sniper, <<0.0, 1.0, 0.0>>)
SET_CAM_ROT(sniper_data.cam_rifle, GET_ENTITY_ROTATION(sniper_data.obj_sniper))
sniper_data.f_current_sniper_fov = 45.0
sniper_data.f_max_zoom = MAX_ZOOM
SET_CAM_FOV(sniper_data.cam_rifle, sniper_data.f_current_sniper_fov)
SET_CAM_MOTION_BLUR_STRENGTH(sniper_data.cam_rifle, 0.02)
// optional params
sniper_data.use_weapon_type = WEAPONTYPE_REMOTESNIPER
sniper_data.i_damage = DEFAULT_DAMAGE
sniper_data.f_rotate_speed = DEFAULT_ROTATE_SPEED
sniper_data.f_scale_rotate_speed_zoom = DEFAULT_SCALE_ROTATE_SPEED_ZOOM
sniper_data.f_shot_distance = 1000.0
sniper_data.b_allow_quit = TRUE
sniper_data.f_lock_pitch_min = -20.0
sniper_data.f_lock_pitch_max = 45.0
//TEMP: Initialise wind values here
//sniper_data.f_base_wind_level = GET_RANDOM_FLOAT_IN_RANGE(-MAX_WIND, MAX_WIND)
sniper_data.f_sniper_wind_angle = GET_RANDOM_FLOAT_IN_RANGE(0.0, 360.0)
//Initialise any random variables
sniper_data.i_sniper_shot_delay = 0
sniper_data.i_sniper_shot_timer = 0
//sniper_data.i_sniper_flicker_timer = 0
sniper_data.b_is_sniping = FALSE
sniper_data.b_bullet_impact_this_frame = FALSE
sniper_data.b_fire_shot = FALSE
sniper_data.b_registered_shot = FALSE
//sniper_data.b_is_thermal_active = FALSE
ELSE
SCRIPT_ASSERT("Remote sniper assets haven't been loaded!")
ENDIF
ENDPROC
//Passes game control to/from the remote sniper rifle
PROC SET_REMOTE_SNIPER_ACTIVE(REMOTE_SNIPER_DATA &sniper_data, BOOL b_is_active)
DISPLAY_RADAR(NOT b_is_active)
DISPLAY_HUD(NOT b_is_active)
//SET_NOISEOVERIDE(b_is_active)
SET_WIDESCREEN_BORDERS(b_is_active, 0)
RENDER_SCRIPT_CAMS(b_is_active, FALSE)
IF IS_PLAYER_PLAYING(PLAYER_ID())
SET_PLAYER_CONTROL(PLAYER_ID(), NOT b_is_active)
ENDIF
CLEAR_HELP()
//SET_TEXT_DRAW_BEFORE_FADE(b_is_active)
sniper_data.b_is_sniping = b_is_active
sniper_data.b_fire_shot = FALSE
sniper_data.b_registered_shot = FALSE
IF b_is_active
//IF sniper_data.b_is_thermal_active
// SET_SEETHROUGH(TRUE)
// CLEAR_TIMECYCLE_MODIFIER()
//ELSE
//SET_SCRIPT_GFX_DRAW_ORDER(GFX_ORDER_BEFORE_HUD)
SET_TIMECYCLE_MODIFIER("RemoteSniper")
SET_SEETHROUGH(FALSE)
//ENDIF
SET_SCRIPT_GFX_DRAW_ORDER(GFX_ORDER_BEFORE_HUD)
SET_SCRIPT_GFX_DRAW_BEHIND_PAUSEMENU(TRUE)
sniper_data.i_zoom_sound_id = GET_SOUND_ID()
sniper_data.i_move_sound_id = GET_SOUND_ID()
//Load in the sound wav for the bullet sound effect Remote_Sniper_Rifle_Fire
WHILE NOT REQUEST_SCRIPT_AUDIO_BANK("MARTIN_1_SNIPER")
WAIT(0)
PRINTSTRING("Waiting for audio script bank loading for MARTIN_1_SNIPER") PRINTNL()
ENDWHILE
ELSE
SET_SEETHROUGH(FALSE)
CLEAR_TIMECYCLE_MODIFIER()
//SET_SPRITES_DRAW_BEFORE_FADE(FALSE
SET_SCRIPT_GFX_DRAW_ORDER(GFX_ORDER_AFTER_HUD)
SET_SCRIPT_GFX_DRAW_BEHIND_PAUSEMENU(FALSE)
//Remove the audio bank for the bullet sound effect
RELEASE_SCRIPT_AUDIO_BANK()
IF sniper_data.b_animPostFX_Started = TRUE
ANIMPOSTFX_STOP("SniperOverlay")
sniper_data.b_animPostFX_Started = FALSE
ENDIF
IF sniper_data.i_zoom_sound_id != 0
STOP_SOUND(sniper_data.i_zoom_sound_id)
RELEASE_SOUND_ID(sniper_data.i_zoom_sound_id)
sniper_data.i_zoom_sound_id = 0
ENDIF
IF sniper_data.i_move_sound_id != 0
STOP_SOUND(sniper_data.i_move_sound_id)
RELEASE_SOUND_ID(sniper_data.i_move_sound_id)
sniper_data.i_move_sound_id = 0
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Cleans up any assets used by the remote sniper rifle
PROC CLEANUP_REMOTE_SNIPER(REMOTE_SNIPER_DATA &sniper_data, BOOL b_remove_scaleform = TRUE)
SET_REMOTE_SNIPER_ACTIVE(sniper_data, FALSE)
IF sniper_data.b_audio_requested
RELEASE_NAMED_SCRIPT_AUDIO_BANK(GET_REMOTE_SNIPER_AUDIO_BANK_NAME())
sniper_data.b_audio_requested = FALSE
ENDIF
IF DOES_CAM_EXIST(sniper_data.cam_rifle)
DESTROY_CAM(sniper_data.cam_rifle)
ENDIF
//IF DOES_CAM_EXIST(sniper_data.cam_recoil)
// DESTROY_CAM(sniper_data.cam_recoil)
//ENDIF
IF DOES_ENTITY_EXIST(sniper_data.obj_sniper)
DELETE_OBJECT(sniper_data.obj_sniper)
ENDIF
IF b_remove_scaleform
IF HAS_SCALEFORM_MOVIE_LOADED(sniper_data.sf_hud)
SET_SCALEFORM_MOVIE_AS_NO_LONGER_NEEDED(sniper_data.sf_hud)
ENDIF
sniper_data.b_hud_requested = FALSE
sniper_data.sf_hud = NULL
ENDIF
ENDPROC
FUNC FLOAT NORMALISE_FLOAT_FOR_SNIPER(FLOAT f_float)
FLOAT f_normalised = f_float
IF f_normalised < 0
f_normalised += 360
ELIF f_normalised > 360
f_normalised -= 360
ENDIF
RETURN f_normalised
ENDFUNC
PROC DRAW_REMOTE_SNIPER_HUD(REMOTE_SNIPER_DATA &sniper_data)
//Border
CONST_FLOAT LINE_THICKNESS 0.002
CONST_FLOAT BORDER_WIDTH 0.9
//CONST_FLOAT BORDER_HEIGHT 0.9
IF NOT sniper_data.b_animPostFX_Started
ANIMPOSTFX_PLAY("SniperOverlay", 0, TRUE)
sniper_data.b_animPostFX_Started = TRUE
ENDIF
//SET_SPRITES_DRAW_BEFORE_FADE(TRUE)
SET_SCRIPT_GFX_DRAW_ORDER(GFX_ORDER_BEFORE_HUD)
SET_SCRIPT_GFX_DRAW_BEHIND_PAUSEMENU(TRUE)
SET_WIDESCREEN_FORMAT(WIDESCREEN_FORMAT_STRETCH)
//Zoom indicator
FLOAT f_zoom_level = 1.0 + (((sniper_data.f_current_sniper_fov - MIN_ZOOM) / (MAX_ZOOM - MIN_ZOOM)) * 9.0)
BEGIN_SCALEFORM_MOVIE_METHOD(sniper_data.sf_hud, "SET_ZOOM_LEVEL")
SCALEFORM_MOVIE_METHOD_ADD_PARAM_FLOAT(f_zoom_level)
END_SCALEFORM_MOVIE_METHOD()
//Rotation indicator
//VECTOR v_rot = GET_ENTITY_ROTATION(sniper_data.obj_sniper)
//CALL_SCALEFORM_MOVIE_METHOD_WITH_NUMBER(sniper_data.sf_hud, "SET_ANGLE", v_rot.z)
// Compass
VECTOR v_rot = GET_ENTITY_ROTATION(sniper_data.obj_sniper)
FLOAT f_rot = NORMALISE_FLOAT_FOR_SNIPER(v_rot.z)
BEGIN_SCALEFORM_MOVIE_METHOD(sniper_data.sf_hud, "SET_COMPASS")
SCALEFORM_MOVIE_METHOD_ADD_PARAM_FLOAT(f_rot)
END_SCALEFORM_MOVIE_METHOD()
//Wind indicator
VECTOR v_wind_direction = GET_WIND_DIRECTION()
FLOAT f_wind_direction = ATAN2(v_wind_direction.x, v_wind_direction.y)
f_wind_direction = NORMALISE_FLOAT_FOR_SNIPER(f_wind_direction)
f_wind_direction -= 360
IF f_wind_direction < 0
f_wind_direction *= -1
ENDIF
FLOAT f_wind_diff = f_wind_direction - f_rot
IF f_wind_diff < -180
f_wind_diff += 360
ELIF f_wind_diff > 180
f_wind_diff -= 360
ENDIF
FLOAT f_wind_right
IF f_wind_diff > 0
f_wind_right = 0.0
ELSE
f_wind_right = 1.0
ENDIF
FLOAT f_wind_strength_mod = f_wind_diff
IF f_wind_strength_mod > 90
f_wind_strength_mod -= 180
ELIF f_wind_strength_mod < -90
f_wind_strength_mod += 180
ENDIF
IF f_wind_strength_mod < 0
f_wind_strength_mod *= -1
ENDIF
f_wind_strength_mod /= 90
/*
PRINTSTRING("dirv = ") PRINTVECTOR(v_wind_direction) PRINTNL()
PRINTSTRING("dirf = ") PRINTFLOAT(f_wind_direction) PRINTNL()
PRINTSTRING("rotation = ") PRINTFLOAT(f_rot) PRINTNL()
PRINTSTRING("difference = ") PRINTFLOAT(f_wind_diff) PRINTNL()
PRINTSTRING("wind strength mod = ") PRINTFLOAT(f_wind_strength_mod) PRINTNL()
PRINTNL() PRINTNL()
*/
FLOAT f_wind_speed = GET_WIND_SPEED()
f_wind_speed = f_wind_speed * 20 * f_wind_strength_mod
BEGIN_SCALEFORM_MOVIE_METHOD(sniper_data.sf_hud, "SET_WIND")
SCALEFORM_MOVIE_METHOD_ADD_PARAM_FLOAT(f_wind_speed)
SCALEFORM_MOVIE_METHOD_ADD_PARAM_FLOAT(f_wind_right)
END_SCALEFORM_MOVIE_METHOD()
//CALL_SCALEFORM_MOVIE_METHOD_WITH_NUMBER(sniper_data.sf_hud, "SET_WIND", f_wind_speed, f_wind_right)
DRAW_SCALEFORM_MOVIE(sniper_data.sf_hud, 0.5, 0.5, 1, 1, 255, 255, 255, 255,1)
SET_SCRIPT_GFX_DRAW_ORDER(GFX_ORDER_AFTER_HUD)
SET_SCRIPT_GFX_DRAW_BEHIND_PAUSEMENU(TRUE)
//SET_SPRITES_DRAW_BEFORE_FADE(FALSE)
ENDPROC
PROC APPLY_TEMP_DEAD_ZONE(INT &iMod)
IF iMod > 0
AND iMod < 10
iMod = 0
ENDIF
IF iMod < 0
AND iMod > -10
iMod = 0
ENDIF
ENDPROC
/// PURPOSE:
/// Updates input and display for the remote sniper rifle
PROC UPDATE_REMOTE_SNIPER(REMOTE_SNIPER_DATA &sniper_data)
CONST_FLOAT ZOOM_SPEED 20.0
CONST_INT MAX_SHOT_DELAY 250
CONST_INT SHOT_COOLDOWN 350
IF DOES_ENTITY_EXIST(sniper_data.obj_sniper)
IF NOT sniper_data.b_is_sniping
IF IS_CONTROL_JUST_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_LB)
SET_REMOTE_SNIPER_ACTIVE(sniper_data, TRUE)
ELSE
//IF NOT HAS_TEXT_BEEN_TRIGGERED(TEXT_SNIPE_HELP_1)
// PRINT_HELP("RS_SNIPEHELP1")
// MARK_TEXT_AS_TRIGGERED(TEXT_SNIPE_HELP_1)
//ENDIF
ENDIF
ELSE
// INT i = 0
//Convert input into rotation/zoom changes
INT i_left_stick_x, i_left_stick_y, i_right_stick_x, i_right_stick_y
IF NOT IS_USING_KEYBOARD_AND_MOUSE(PLAYER_CONTROL)
GET_CONTROL_VALUE_OF_ANALOGUE_STICKS(i_left_stick_x, i_left_stick_y, i_right_stick_x, i_right_stick_y)
ELSE
i_left_stick_x = FLOOR(GET_CONTROL_UNBOUND_NORMAL(FRONTEND_CONTROL, INPUT_SCRIPT_LEFT_AXIS_X) * 127)
i_left_stick_y = FLOOR(GET_CONTROL_UNBOUND_NORMAL(FRONTEND_CONTROL, INPUT_SCRIPT_LEFT_AXIS_Y) * 127)
i_right_stick_x = FLOOR(GET_CONTROL_UNBOUND_NORMAL(FRONTEND_CONTROL, INPUT_LOOK_LR) * 127)
i_right_stick_y = FLOOR(GET_CONTROL_UNBOUND_NORMAL(FRONTEND_CONTROL, INPUT_LOOK_UD) * 127)
ENDIF
//i_left_stick_x = FLOOR(GET_CONTROL_NORMAL(FRONTEND_CONTROL, INPUT_SNIPER_ZOOM_IN) * 127)
i_left_stick_y = FLOOR(GET_CONTROL_NORMAL(FRONTEND_CONTROL, INPUT_SNIPER_ZOOM) * 127)
// Temp deadzone (pad only)
IF NOT IS_USING_KEYBOARD_AND_MOUSE(PLAYER_CONTROL)
APPLY_TEMP_DEAD_ZONE(i_left_stick_x)
APPLY_TEMP_DEAD_ZONE(i_left_stick_y)
APPLY_TEMP_DEAD_ZONE(i_right_stick_x)
APPLY_TEMP_DEAD_ZONE(i_right_stick_y)
ENDIF
FLOAT f_zoom_modifier = (sniper_data.f_current_sniper_fov - MIN_ZOOM) / (MAX_ZOOM - MIN_ZOOM) //Changes turn/zoom speed depending on how far the scope is zoomed in
//Modify input based on controller settings: can swap zoom/aim to other sticks, and invert y-look
INT i_rot_x_input = i_right_stick_y
INT i_rot_z_input = i_right_stick_x
INT i_zoom_input = i_left_stick_y
IF NOT IS_USING_KEYBOARD_AND_MOUSE(PLAYER_CONTROL)
IF IS_SNIPER_INVERTED()
i_rot_x_input = i_left_stick_y
i_rot_z_input = i_left_stick_x
i_zoom_input = i_right_stick_y
ENDIF
IF IS_LOOK_INVERTED()
i_rot_x_input = -i_rot_x_input
ENDIF
ENDIF
//Rotation
FLOAT f_x_rot_vel = -(i_rot_x_input / 128.0) * sniper_data.f_rotate_speed //(ROTATE_SPEED / (1.0 + (f_zoom_modifier * 10.0)))
FLOAT f_z_rot_vel = -(i_rot_z_input / 128.0) * sniper_data.f_rotate_speed //(ROTATE_SPEED / (1.0 + (f_zoom_modifier * 10.0)))
//When the rifle is at high zoom levels, reduce the turn speed for better accuracy.
IF sniper_data.f_current_sniper_fov <= sniper_data.f_scale_rotate_speed_zoom
FLOAT f_turn_modifier = (sniper_data.f_current_sniper_fov - MAX_ZOOM) / (sniper_data.f_scale_rotate_speed_zoom - MAX_ZOOM)
IF f_turn_modifier < 0.05
f_turn_modifier = 0.05
ENDIF
f_x_rot_vel *= f_turn_modifier
f_z_rot_vel *= f_turn_modifier
ENDIF
VECTOR v_obj_rot = GET_ENTITY_ROTATION(sniper_data.obj_sniper)
// rotate Z
BOOL bRotatedZ = TRUE
IF ABSI(i_rot_z_input) > 0
IF NOT IS_USING_KEYBOARD_AND_MOUSE(PLAYER_CONTROL)
v_obj_rot = v_obj_rot +@ <<0.0, 0.0, f_z_rot_vel>>
ELSE
v_obj_rot = v_obj_rot + (<<0.0, 0.0, f_z_rot_vel>> * 0.5)//0.1)
ENDIF
IF sniper_data.b_lock_rotation
IF v_obj_rot.z <= sniper_data.f_lock_rotation_min
v_obj_rot.z = sniper_data.f_lock_rotation_min
bRotatedZ = FALSE
ENDIF
IF v_obj_rot.z >= sniper_data.f_lock_rotation_max
v_obj_rot.z = sniper_data.f_lock_rotation_max
bRotatedZ = FALSE
ENDIF
ENDIF
ELSE
bRotatedZ = FALSE
ENDIF
// rotate X
BOOL bRotatedX = TRUE
IF ABSI(i_rot_x_input) > 0
IF NOT IS_USING_KEYBOARD_AND_MOUSE(PLAYER_CONTROL)
v_obj_rot = v_obj_rot +@ <<f_x_rot_vel, 0.0, 0.0>>
ELSE
v_obj_rot = v_obj_rot + (<<f_x_rot_vel, 0.0, 0.0>> * 0.5)//0.1)
ENDIF
IF v_obj_rot.x <= sniper_data.f_lock_pitch_min
v_obj_rot.x = sniper_data.f_lock_pitch_min
bRotatedX = FALSE
ENDIF
IF v_obj_rot.x >= sniper_data.f_lock_pitch_max
v_obj_rot.x = sniper_data.f_lock_pitch_max
bRotatedX = FALSE
ENDIF
ELSE
bRotatedX = FALSE
ENDIF
SET_ENTITY_ROTATION(sniper_data.obj_sniper, v_obj_rot)
//Future rotation: estimate where the rotation will be next frame. This helps with placing HUD elements (the HUD is always a frame behind)
//sniper_data.v_sniper_future_rot = v_obj_rot +@ <<f_x_rot_vel, 0.0, f_z_rot_vel>>
//sniper_data.v_sniper_future_rot.x = clamp(sniper_data.v_sniper_future_rot.x, MIN_ROT_X, MAX_ROT_X)
//Rotation audio
IF bRotatedX OR bRotatedZ
IF HAS_SOUND_FINISHED(sniper_data.i_move_sound_id)
PLAY_SOUND_FRONTEND(sniper_data.i_move_sound_id, "Remote_Sniper_Rifle_Move")
ENDIF
SET_VARIABLE_ON_SOUND(sniper_data.i_move_sound_id, "movement_speed", TO_FLOAT(ABSI(i_rot_z_input) + ABSI(i_rot_x_input)) / 256.0)
IF i_rot_x_input < 0
SET_VARIABLE_ON_SOUND(sniper_data.i_move_sound_id, "y_direction", 1.0)
ELIF i_rot_x_input > 0
SET_VARIABLE_ON_SOUND(sniper_data.i_move_sound_id, "y_direction", 0.0)
ELSE
SET_VARIABLE_ON_SOUND(sniper_data.i_move_sound_id, "y_direction", 0.5)
ENDIF
ELSE
IF NOT HAS_SOUND_FINISHED(sniper_data.i_move_sound_id)
STOP_SOUND(sniper_data.i_move_sound_id)
ENDIF
ENDIF
//Zoom
FLOAT f_zoom_vel = (i_zoom_input / 128.0) * (ZOOM_SPEED / (1.0 + (f_zoom_modifier * 2.0)))
BOOL bZoomed = TRUE
IF ABSI(i_zoom_input) > 0
sniper_data.f_current_sniper_fov = sniper_data.f_current_sniper_fov +@ f_zoom_vel
IF sniper_data.f_current_sniper_fov <= sniper_data.f_max_zoom
sniper_data.f_current_sniper_fov = sniper_data.f_max_zoom
bZoomed = FALSE
ENDIF
IF sniper_data.f_current_sniper_fov >= MIN_ZOOM
sniper_data.f_current_sniper_fov = MIN_ZOOM
bZoomed = FALSE
ENDIF
ELSE
bZoomed = FALSE
ENDIF
//Zoom audio
IF bZoomed
IF HAS_SOUND_FINISHED(sniper_data.i_zoom_sound_id)
PLAY_SOUND_FRONTEND(sniper_data.i_zoom_sound_id, "Remote_Sniper_Rifle_Zoom")
ENDIF
SET_VARIABLE_ON_SOUND(sniper_data.i_zoom_sound_id, "zoom_speed", TO_FLOAT(ABSI(i_zoom_input)) / 128.0)
ELSE
IF NOT HAS_SOUND_FINISHED(sniper_data.i_zoom_sound_id)
STOP_SOUND(sniper_data.i_zoom_sound_id)
ENDIF
ENDIF
//Adjust the wind value based on which way the rifle is facing (e.g. if it is facing towards the wind there won't be any effect on the bullet direction)
FLOAT f_rifle_rot = v_obj_rot.z
IF f_rifle_rot < 0.0
f_rifle_rot += 360.0
ENDIF
FLOAT f_angle_diff = ABSF(sniper_data.f_sniper_wind_angle - f_rifle_rot)
IF f_angle_diff > 180.0
f_angle_diff = 360.0 - f_angle_diff
ENDIF
//FLOAT f_wind_to_rifle_angle_ratio = -((f_angle_diff - 90.0) / 90.0) //This value is between -1.0 and 1.0
//sniper_data.f_adjusted_wind_level = sniper_data.f_base_wind_level * f_wind_to_rifle_angle_ratio
//Shake the wind value a bit
IF sniper_data.f_wind_offset = sniper_data.f_desired_wind_offset
sniper_data.f_desired_wind_offset = GET_RANDOM_FLOAT_IN_RANGE(-0.01, 0.01)
sniper_data.f_wind_offset_speed = GET_RANDOM_FLOAT_IN_RANGE(0.002, 0.008)
ELSE
CONVERGE_VALUE(sniper_data.f_wind_offset, sniper_data.f_desired_wind_offset, sniper_data.f_wind_offset_speed)
ENDIF
//sniper_data.f_randomised_wind_level = sniper_data.f_adjusted_wind_level + sniper_data.f_wind_offset
//Activate/deactivate thermal imaging
/*
IF NOT sniper_data.b_is_thermal_active
IF IS_CONTROL_JUST_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_DOWN)
SET_SEETHROUGH(TRUE)
CLEAR_TIMECYCLE_MODIFIER()
sniper_data.b_is_thermal_active = TRUE
ENDIF
ELSE
IF IS_CONTROL_JUST_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_DOWN)
SET_SEETHROUGH(FALSE)
SET_TIMECYCLE_MODIFIER("blackNwhite")
sniper_data.b_is_thermal_active = FALSE
ENDIF
ENDIF
*/
//Recoil
//VECTOR v_recoil_cam_rot_offset = GET_CAM_ROT(sniper_data.cam_recoil)
//FLOAT f_fov_offset = v_recoil_cam_rot_offset.x
//IF f_fov_offset < 0.0
// f_fov_offset = 0.0
//ENDIF
sniper_data.f_current_fov_plus_recoil = sniper_data.f_current_sniper_fov //+ f_fov_offset
//Update the camera
SET_CAM_ROT(sniper_data.cam_rifle, v_obj_rot)
SET_CAM_FOV(sniper_data.cam_rifle, sniper_data.f_current_fov_plus_recoil)
//Draw HUD
DRAW_REMOTE_SNIPER_HUD(sniper_data)
//Shoot bullets
IF IS_DISABLED_CONTROL_JUST_PRESSED(PLAYER_CONTROL, INPUT_ATTACK)
sniper_data.b_registered_shot = TRUE
sniper_data.i_register_time = GET_GAME_TIMER() + SHOT_COOLDOWN
ELSE
IF sniper_data.b_registered_shot
IF GET_GAME_TIMER() >= sniper_data.i_register_time
sniper_data.b_registered_shot = FALSE
ENDIF
ENDIF
ENDIF
IF sniper_data.b_registered_shot
IF GET_GAME_TIMER() >= sniper_data.i_sniper_shot_timer
sniper_data.v_last_shot_rot = v_obj_rot
sniper_data.v_last_shot_dir = CONVERT_ROTATION_TO_DIRECTION_VECTOR(v_obj_rot)
sniper_data.i_sniper_shot_delay = 0
sniper_data.i_sniper_shot_timer = GET_GAME_TIMER() + SHOT_COOLDOWN
PLAY_SOUND_FROM_ENTITY(-1, "Remote_Sniper_Rifle_Fire", sniper_data.obj_sniper)
sniper_data.b_fire_shot = TRUE
ENDIF
ENDIF
IF sniper_data.b_fire_shot
IF GET_GAME_TIMER() >= sniper_data.i_sniper_shot_delay
VECTOR v_pos = GET_CAM_COORD(sniper_data.cam_rifle)
sniper_data.i_sniper_shot_delay = 0
SHOOT_SINGLE_BULLET_BETWEEN_COORDS(v_pos, v_pos + (sniper_data.v_last_shot_dir * sniper_data.f_shot_distance), sniper_data.i_damage, TRUE, sniper_data.use_weapon_type, PLAYER_PED_ID(), FALSE)
sniper_data.b_fire_shot = FALSE
ENDIF
ENDIF
//Handle bullet impacts: store the information,
sniper_data.v_last_impact_pos = <<0.0, 0.0, 0.0>>
sniper_data.b_bullet_impact_this_frame = GET_PED_LAST_WEAPON_IMPACT_COORD(PLAYER_PED_ID(), sniper_data.v_last_impact_pos)
//Handle quitting
IF sniper_data.b_allow_quit
IF IS_CONTROL_JUST_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_LB)
SET_REMOTE_SNIPER_ACTIVE(sniper_data, FALSE)
ELSE
//IF NOT HAS_TEXT_BEEN_TRIGGERED(TEXT_SNIPE_HELP_2)
// PRINT_HELP("RS_SNIPEHELP2")
// MARK_TEXT_AS_TRIGGERED(TEXT_SNIPE_HELP_2)
//ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Draws a box around the given ped (this will only work if thermal vision is currently active)
PROC HIGHLIGHT_PED_ON_REMOTE_SNIPER_HUD(REMOTE_SNIPER_DATA &sniper_data, PED_INDEX &ped)
UNUSED_PARAMETER(sniper_data)
UNUSED_PARAMETER(ped)
/*
IF sniper_data.b_is_thermal_active
IF NOT IS_PED_INJURED(ped)
IF WAS_PED_SKELETON_UPDATED(ped)
IF IS_ENTITY_ON_SCREEN(ped)
VECTOR v_top_pos, v_bottom_pos, v_left_pos, v_right_pos, v_back_pos, v_front_pos
VECTOR v_cam_pos = GET_CAM_COORD(sniper_data.cam_rifle)
VECTOR v_cam_rot = GET_ENTITY_ROTATION(sniper_data.obj_sniper)
//These modifiers are fudged, and are used to make sure the tags are in the right position at any fov level
FLOAT f_offset_modifier_x = sniper_data.f_current_fov_plus_recoil * 1.345
FLOAT f_offset_modifier_y = sniper_data.f_current_fov_plus_recoil
//Get a set of coordinates that surround the the current ped
v_left_pos = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(ped, <<0.45, 0.0, 0.0>>)
v_right_pos = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(ped, <<-0.45, 0.0, 0.0>>)
v_back_pos = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(ped, <<0.0, -0.3, 0.0>>)
v_front_pos = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(ped, <<0.0, 0.3, 0.0>>)
v_top_pos = GET_PED_BONE_COORDS(ped, BONETAG_HEAD, <<0.0, 0.0, 0.0>>) + <<0.0, 0.0, 0.2>>
VECTOR v_left_foot_pos = GET_PED_BONE_COORDS(ped, BONETAG_L_FOOT, <<0.0, 0.0, 0.0>>) - <<0.0, 0.0, 0.2>>
VECTOR v_right_foot_pos = GET_PED_BONE_COORDS(ped, BONETAG_R_FOOT, <<0.0, 0.0, 0.0>>) - <<0.0, 0.0, 0.2>>
IF v_left_foot_pos.z < v_right_foot_pos.z
v_bottom_pos = v_left_foot_pos
ELSE
v_bottom_pos = v_right_foot_pos
ENDIF
*/
/*VECTOR v_ped_offsets[7]
GET_SCREEN_COORD_FROM_WORLD_COORD(GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(ped, <<0.45, 0.0, 0.0>>), v_ped_offsets[0].x, v_ped_offsets[0].y) //Left
GET_SCREEN_COORD_FROM_WORLD_COORD(GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(ped, <<-0.45, 0.0, 0.0>>), v_ped_offsets[1].x, v_ped_offsets[1].y) //Right
GET_SCREEN_COORD_FROM_WORLD_COORD(GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(ped, <<0.0, -0.3, 0.0>>), v_ped_offsets[2].x, v_ped_offsets[2].y) //Front
GET_SCREEN_COORD_FROM_WORLD_COORD(GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(ped, <<0.0, 0.3, 0.0>>), v_ped_offsets[3].x, v_ped_offsets[3].y) //Back
GET_SCREEN_COORD_FROM_WORLD_COORD(GET_PED_BONE_COORDS(ped, BONETAG_HEAD, <<0.0, 0.0, 0.0>>) + <<0.0, 0.0, 0.2>>, v_ped_offsets[4].x, v_ped_offsets[4].y) //Head
GET_SCREEN_COORD_FROM_WORLD_COORD(GET_PED_BONE_COORDS(ped, BONETAG_L_FOOT, <<0.0, 0.0, 0.0>>) - <<0.0, 0.0, 0.2>>, v_ped_offsets[5].x, v_ped_offsets[5].y) //Left foot
GET_SCREEN_COORD_FROM_WORLD_COORD(GET_PED_BONE_COORDS(ped, BONETAG_R_FOOT, <<0.0, 0.0, 0.0>>) - <<0.0, 0.0, 0.2>>, v_ped_offsets[6].x, v_ped_offsets[6].y) //Right foot
FLOAT f_left_x = -1.0
FLOAT f_right_x = -1.0
FLOAT f_top_y = -1.0
FLOAT f_bottom_y = -1.0
//Find the box size that covers all of the offsets calculated above
INT i_num_values_out_of_bounds = 0
INT i = 0
REPEAT COUNT_OF(v_ped_offsets) i
IF v_ped_offsets[i].x != -1.0
IF f_left_x = -1.0 OR v_ped_offsets[i].x < f_left_x
f_left_x = v_ped_offsets[i].x
ELIF f_right_x = -1.0 OR v_ped_offsets[i].x > f_right_x
f_right_x = v_ped_offsets[i].x
ENDIF
ELSE
i_num_values_out_of_bounds++
ENDIF
IF v_ped_offsets[i].y != -1.0
IF f_top_y = -1.0 OR v_ped_offsets[i].y < f_top_y
f_top_y = v_ped_offsets[i].y
ELIF f_bottom_y = -1.0 OR v_ped_offsets[i].y > f_bottom_y
f_bottom_y = v_ped_offsets[i].y
ENDIF
ENDIF
ENDREPEAT*/
/*
//Work out the difference between these coords and the current camera position
VECTOR v_top_pos_diff = v_top_pos - v_cam_pos
VECTOR v_bottom_pos_diff = v_bottom_pos - v_cam_pos
VECTOR v_left_pos_diff = v_left_pos - v_cam_pos
VECTOR v_right_pos_diff = v_right_pos - v_cam_pos
VECTOR v_back_pos_diff = v_back_pos - v_cam_pos
VECTOR v_front_pos_diff = v_front_pos - v_cam_pos
//Use the differences to work out the angles the camera would have to face for these coordinates to be dead centre
FLOAT f_left_heading = GET_HEADING_FROM_VECTOR_2D(v_left_pos_diff.x, v_left_pos_diff.y)
FLOAT f_right_heading = GET_HEADING_FROM_VECTOR_2D(v_right_pos_diff.x, v_right_pos_diff.y)
FLOAT f_back_heading = GET_HEADING_FROM_VECTOR_2D(v_back_pos_diff.x, v_back_pos_diff .y)
FLOAT f_front_heading = GET_HEADING_FROM_VECTOR_2D(v_front_pos_diff.x, v_front_pos_diff.y)
FLOAT f_top_pitch = ATAN2(v_top_pos_diff.z, DISTANCE_BETWEEN_COORDS(v_top_pos, v_cam_pos, TRUE, TRUE))
FLOAT f_bottom_pitch = ATAN2(v_bottom_pos_diff.z, DISTANCE_BETWEEN_COORDS(v_bottom_pos, v_cam_pos, TRUE, TRUE))
//For the box width: need to consider what the heading of the ped is. If they're facing side on to the camera
//the front and back offsets should be used
IF ABSF(f_back_heading - f_front_heading) > ABSF(f_left_heading - f_right_heading)
f_left_heading = f_back_heading
f_right_heading = f_front_heading
ENDIF
//Convert the differences between the required angles and the current camera angle into screen offsets
FLOAT f_x_left_offset = (f_left_heading - v_cam_rot.z) / f_offset_modifier_x
FLOAT f_x_right_offset = (f_right_heading - v_cam_rot.z) / f_offset_modifier_x
FLOAT f_top_y_offset = (f_top_pitch - v_cam_rot.x) / f_offset_modifier_y
FLOAT f_bottom_y_offset = (f_bottom_pitch - v_cam_rot.x) / f_offset_modifier_y
//These just improve readability of the rectangle calculations
FLOAT f_left_x = 0.5 - f_x_left_offset
FLOAT f_right_x = 0.5 - f_x_right_offset
FLOAT f_top_y = 0.5 - f_top_y_offset
FLOAT f_bottom_y = 0.5 - f_bottom_y_offset
SET_WIDESCREEN_FORMAT(WIDESCREEN_FORMAT_CENTRE)
//Top and bottom lines
DRAW_RECT((f_left_x + f_right_x) / 2.0, f_top_y, ABSF(f_left_x - f_right_x), 0.002, 255, 0, 0, 128)
DRAW_RECT((f_left_x + f_right_x) / 2.0, f_bottom_y, ABSF(f_left_x - f_right_x), 0.002, 255, 0, 0, 128)
//Side lines
DRAW_RECT(f_left_x, (f_top_y + f_bottom_y) / 2.0, 0.002, ABSF(f_top_y - f_bottom_y) + 0.002, 255, 0, 0, 128)
DRAW_RECT(f_right_x, (f_top_y + f_bottom_y) / 2.0, 0.002, ABSF(f_top_y - f_bottom_y) + 0.002, 255, 0, 0, 128)
ENDIF
ENDIF
ENDIF
ENDIF
*/
ENDPROC
FUNC BOOL HAS_REMOTE_SNIPER_BULLET_INDIRECTLY_HIT_PED(REMOTE_SNIPER_DATA &sniper_data, PED_INDEX &ped)
IF NOT IS_PED_INJURED(ped)
IF sniper_data.b_bullet_impact_this_frame
FLOAT f_dist = DISTANCE_BETWEEN_COORDS(sniper_data.v_last_impact_pos, GET_ENTITY_COORDS(ped), FALSE, FALSE)
IF f_dist < 25.0
VECTOR v_top_pos, v_bottom_pos, v_left_pos, v_right_pos, v_back_pos, v_front_pos
VECTOR v_cam_pos = GET_CAM_COORD(sniper_data.cam_rifle)
IF WAS_PED_SKELETON_UPDATED(ped)
//Get a set of coordinates that surround the the current ped
v_left_pos = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(ped, <<0.45, 0.0, 0.0>>)
v_right_pos = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(ped, <<-0.45, 0.0, 0.0>>)
v_back_pos = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(ped, <<0.0, -0.3, 0.0>>)
v_front_pos = GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(ped, <<0.0, 0.3, 0.0>>)
v_top_pos = GET_PED_BONE_COORDS(ped, BONETAG_HEAD, <<0.0, 0.0, 0.0>>) + <<0.0, 0.0, 0.2>>
VECTOR v_left_foot_pos = GET_PED_BONE_COORDS(ped, BONETAG_L_FOOT, <<0.0, 0.0, 0.0>>) - <<0.0, 0.0, 0.2>>
VECTOR v_right_foot_pos = GET_PED_BONE_COORDS(ped, BONETAG_R_FOOT, <<0.0, 0.0, 0.0>>) - <<0.0, 0.0, 0.2>>
IF v_left_foot_pos.z < v_right_foot_pos.z
v_bottom_pos = v_left_foot_pos
ELSE
v_bottom_pos = v_right_foot_pos
ENDIF
//Work out the difference between these coords and the current camera position
VECTOR v_top_pos_diff = v_top_pos - v_cam_pos
VECTOR v_bottom_pos_diff = v_bottom_pos - v_cam_pos
VECTOR v_left_pos_diff = v_left_pos - v_cam_pos
VECTOR v_right_pos_diff = v_right_pos - v_cam_pos
VECTOR v_back_pos_diff = v_back_pos - v_cam_pos
VECTOR v_front_pos_diff = v_front_pos - v_cam_pos
//Use the differences to work out the angles the camera would have to face for these coordinates to be dead centre
FLOAT f_left_heading = GET_HEADING_FROM_VECTOR_2D(v_left_pos_diff.x, v_left_pos_diff.y)
FLOAT f_right_heading = GET_HEADING_FROM_VECTOR_2D(v_right_pos_diff.x, v_right_pos_diff.y)
FLOAT f_back_heading = GET_HEADING_FROM_VECTOR_2D(v_back_pos_diff.x, v_back_pos_diff .y)
FLOAT f_front_heading = GET_HEADING_FROM_VECTOR_2D(v_front_pos_diff.x, v_front_pos_diff.y)
FLOAT f_top_pitch = ATAN2(v_top_pos_diff.z, DISTANCE_BETWEEN_COORDS(v_top_pos, v_cam_pos, TRUE, TRUE))
FLOAT f_bottom_pitch = ATAN2(v_bottom_pos_diff.z, DISTANCE_BETWEEN_COORDS(v_bottom_pos, v_cam_pos, TRUE, TRUE))
//For the box width: need to consider what the heading of the ped is. If they're facing side on to the camera
//the front and back offsets should be used
IF ABSF(f_back_heading - f_front_heading) > ABSF(f_left_heading - f_right_heading)
f_left_heading = f_back_heading
f_right_heading = f_front_heading
ENDIF
IF f_left_heading > f_right_heading
SWAP_VALUES(f_left_heading, f_right_heading)
ENDIF
IF f_bottom_pitch > f_top_pitch
SWAP_VALUES(f_bottom_pitch, f_top_pitch)
ENDIF
//If the shot direction lies within the ped bounds, it should hit the ped as well
IF sniper_data.v_last_shot_rot.x < f_top_pitch
IF sniper_data.v_last_shot_rot.x > f_bottom_pitch
IF sniper_data.v_last_shot_rot.z < f_right_heading
IF sniper_data.v_last_shot_rot.z > f_left_heading
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Updates a ped's state on the remote sniper HUD, and checks for bullet impacts on the ped.
PROC CHECK_PED_WITH_REMOTE_SNIPER(REMOTE_SNIPER_DATA &sniper_data, PED_INDEX &ped)
IF NOT IS_PED_INJURED(ped)
//Do thermal imaging on HUD
//HIGHLIGHT_PED_ON_REMOTE_SNIPER_HUD(sniper_data, ped)
//Handle bullet impacts
VECTOR v_blood_pos = <<0.0, 0.0, 0.0>>
IF HAS_ENTITY_BEEN_DAMAGED_BY_ENTITY(ped, PLAYER_PED_ID())
//Direct hit
PED_BONETAG damaged_bone = BONETAG_ROOT
GET_PED_LAST_DAMAGE_BONE(ped, damaged_bone)
IF WAS_PED_SKELETON_UPDATED(ped)
v_blood_pos = GET_PED_BONE_COORDS(ped, damaged_bone, <<0.0, 0.0, 0.0>>)
ELSE
v_blood_pos = GET_ENTITY_COORDS(ped)
ENDIF
ELSE
//Check if the ped has been hit indirectly
IF HAS_REMOTE_SNIPER_BULLET_INDIRECTLY_HIT_PED(sniper_data, ped)
v_blood_pos = GET_ENTITY_COORDS(ped)
ENDIF
ENDIF
//If the ped was hit either directly or through something else, fake a messy death
IF v_blood_pos.x != 0.0
//START_PARTICLE_FX_NON_LOOPED_AT_COORD("blood_fall", v_blood_pos, <<0.0, 0.0, 0.0>>, 10.0)
//START_PARTICLE_FX_NON_LOOPED_AT_COORD("blood_fall", v_blood_pos + <<0.3, 0.3, 0.3>>, <<0.0, 0.0, 0.0>>, 10.0)
//START_PARTICLE_FX_NON_LOOPED_AT_COORD("blood_fall", v_blood_pos - <<0.3, 0.3, 0.3>>, <<0.0, 0.0, 0.0>>, 10.0)
IF NOT IS_PED_IN_ANY_VEHICLE(ped)
SET_PED_TO_RAGDOLL(ped, 100, 1000, TASK_NM_BALANCE, FALSE, FALSE, FALSE)
APPLY_FORCE_TO_ENTITY(ped, APPLY_TYPE_IMPULSE, sniper_data.v_last_shot_dir * 4.0, v_blood_pos - GET_ENTITY_COORDS(ped),
GET_PED_RAGDOLL_BONE_INDEX(ped, RAGDOLL_PELVIS), FALSE, FALSE, TRUE)
ENDIF
SET_ENTITY_HEALTH(ped, 0)
ENDIF
ENDIF
ENDPROC
PROC CHECK_VEHICLE_WITH_REMOTE_SNIPER(REMOTE_SNIPER_DATA &sniper_data, VEHICLE_INDEX &veh)
IF sniper_data.b_is_sniping
IF NOT IS_ENTITY_DEAD(veh)
IF HAS_ENTITY_BEEN_DAMAGED_BY_ENTITY(veh, PLAYER_PED_ID())
IF GET_VEHICLE_ENGINE_HEALTH(veh) > -250.0
SET_VEHICLE_ENGINE_HEALTH(veh, -250.0)
ELSE
EXPLODE_VEHICLE(veh)
ENDIF
ENDIF
ENDIF
ENDIF
ENDPROC