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 +@ <> ELSE v_obj_rot = v_obj_rot + (<> * 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 +@ <> //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