Files
2025-09-29 00:52:08 +02:00

989 lines
32 KiB
Python
Executable File

//////////////////////////////////////////////////////////////////////////////////////////
// //
// SCRIPT NAME : ambient_diving.sc //
// AUTHOR : David Roberts //
// DESCRIPTION : Sets up submarine wreckage around the map for the player //
// to collect via diving. //
// //
//////////////////////////////////////////////////////////////////////////////////////////
//Compile out Title Update changes to header functions.
//Must be before includes.
//CONST_INT USE_TU_CHANGES 0 // Removed by Kenneth R.
//----------------------
// INCLUDES
//----------------------
USING "email_public.sch"
USING "rgeneral_include.sch"
USING "RC_Helper_Functions.sch"
USING "CompletionPercentage_public.sch"
USING "savegame_public.sch"
USING "scrap_common.sch"
//----------------------
// CONSTS
//----------------------
CONST_INT XVERSION_NUMBER 107
CONST_INT DS_NOT_IN_DINGHY 0 // Boat state machine
CONST_INT DS_IN_DINGHY 1
CONST_INT BLIP_WRECK_DISTANCE 200 // Range at which wreck sites can be seen on the main map
CONST_INT DINGHY_CLEANUP_RANGE 250 // Range at which a registered unmanned dinghy will be cleaned up normally
CONST_INT DINGHY_CLEANUP_RANGE_IN_WATER 500 // Range at which dinghy will be cleaned up when the player is still in the water
CONST_FLOAT SONAR_MIN_SIZE_BOAT 20.0 // Minimum sonar blip size when in the dinghy
CONST_FLOAT SONAR_MIN_SIZE_SWIM 10.0 // Minimum sonar blip size when swimming
CONST_FLOAT SONAR_UPDATE_SPEED 0.025 // Increase this to make the sonar update faster
//----------------------
// ENUMS
//----------------------
ENUM MISSION_STATUS
MS_INIT,
MS_COLLECT_PICKUPS,
MS_POST_COMPLETION,
MS_CLEANUP
ENDENUM
//---------------------
// VARIABLES
//----------------------
SCRAP_MISSION_DATA sMissionData
SCRAP_PICKUP_DATA sPickupData[NUMBER_OF_DIVING_SCRAPS]
MISSION_STATUS eStage = MS_INIT
SCALEFORM_INDEX siMessage
INT iNearestScrap = -1
INT iMessageStage = 0
BLIP_INDEX biWreckSite
INT iBlippedSite
BLIP_INDEX biSonarBlip
BLIP_INDEX biSonarOutline[4]
FLOAT fExtraBlipSize[4]
FLOAT fCurrentRadius
FLOAT fTargetRadius
FLOAT fSonarIncrement
BOOL bSonarUpdate = TRUE
BOOL bCallRegistered = FALSE
INT iSonarID = GET_SOUND_ID()
VEHICLE_INDEX viCurrentDinghy
BLIP_INDEX biDinghy
INT iDinghyStatus = DS_NOT_IN_DINGHY
BOOL bOnMissionCleanupDone = FALSE
#IF IS_DEBUG_BUILD
WIDGET_GROUP_ID mWidgetGroup
BOOL bMapAllScraps
BOOL bScrapsAreBlipped
BOOL bCollectAllScraps
BOOL bResetScrapCollection
BOOL bDebugAllowWarping
BOOL bWarpToScrap[NUMBER_OF_DIVING_SCRAPS]
BOOL bDebugQuitScript
BOOL bSimulateScrapCollect
INT iSimulateScrapCollect = 1
#ENDIF
//----------------------
// FUNCTIONS
//----------------------
/// PURPOSE:
/// Set the correct stage and revert it to setup
PROC SET_STAGE(MISSION_STATUS stage)
eStage = stage
ENDPROC
/// PURPOSE:
/// Returns the coordinate of the scrap piece specified by the index.
FUNC VECTOR GET_DIVING_COORD(INT index)
SWITCH index
CASE 0 RETURN << -1036.73, 6735.72, -100.52 >> BREAK
CASE 1 RETURN << -908.86, 6655.98, -34.35 >> BREAK
CASE 2 RETURN << -985.14, 6697.71, -41.57 >> BREAK
CASE 3 RETURN << 1825.73, -2920.67, -36.82 >> BREAK
CASE 4 RETURN << 1772.11, -2967.72, -46.81 >> BREAK
CASE 5 RETURN << 3198.59, -385.05, -31.49 >> BREAK
CASE 6 RETURN << 3170.43, -302.21, -25.99 >> BREAK
CASE 7 RETURN << 3157.44, -268.54, -28.07 >> BREAK
CASE 8 RETURN << -3180.20, 3010.90, -37.60 >> BREAK
CASE 9 RETURN << -3178.30, 3044.86, -39.96 >> BREAK
CASE 10 RETURN << 910.48, -3471.21, -17.57 >> BREAK
CASE 11 RETURN << 1338.76, -3041.59, -19.23 >> BREAK
CASE 12 RETURN << 1153.38, -2864.51, -18.96 >> BREAK
CASE 13 RETURN << 958.56, -2847.78, -22.05 >> BREAK
CASE 14 RETURN << 782.28, -2872.81, -9.577 >> BREAK
CASE 15 RETURN << 581.28, -2471.50, -9.44 >> BREAK
CASE 16 RETURN << 636.56, -2214.49, -7.87 >> BREAK
CASE 17 RETURN << 371.06, -3226.67, -19.60 >> BREAK
CASE 18 RETURN << 689.69, -3451.07, -27.85 >> BREAK
CASE 19 RETURN << 180.17, -2255.91, -2.54 >> BREAK
CASE 20 RETURN << -691.64, -2836.86, -15.67 >> BREAK
CASE 21 RETURN << -3397.50, 3717.52, -86.14 >> BREAK
CASE 22 RETURN << -3357.11, 3710.79, -96.14 >> BREAK
CASE 23 RETURN << -3282.21, 3682.60, -82.87 >> BREAK
CASE 24 RETURN << -3256.66, 3672.29, -35.06 >> BREAK
CASE 25 RETURN << -3142.19, 3625.95, -26.31 >> BREAK
CASE 26 RETURN << 3271.34, 6420.78, -50.78 >> BREAK
CASE 27 RETURN << 3237.83, 6487.44, -43.90 >> BREAK
CASE 28 RETURN << 1772.10, -2997.12, -50.44 >> BREAK
CASE 29 RETURN << 3207.00, -415.17, -32.01 >> BREAK
ENDSWITCH
SCRIPT_ASSERT("Diving: Invalid index passed to GET_DIVING_COORD()")
RETURN << 0.0, 0.0, 0.0 >>
ENDFUNC
/// PURPOSE:
/// Setup DIVING scrap position and headings - set global variable for the mission being active
PROC INITIALISE_DIVING_SCRAPS()
// Initialise diving scraps
INT i
REPEAT NUMBER_OF_DIVING_SCRAPS i
sPickupData[i].vCoords = GET_DIVING_COORD(i)
sPickupData[i].bActive = FALSE
ENDREPEAT
sPickupData[0].vRot = << -16.40, 78.53, -146.67 >>
sPickupData[1].vRot = << 0.0, 45.0, 10.0 >>
sPickupData[2].vRot = << 0.72, 65.20, -50.26 >>
sPickupData[3].vRot = << 0.00, 15.00, -132.16 >>
sPickupData[4].vRot = << 166.37, -40.00, 168.81 >>
sPickupData[5].vRot = << 90.00, 56.20, 90.00 >>
sPickupData[6].vRot = << -20.00, 0.00, 89.95 >>
sPickupData[7].vRot = << 4.08, 54.90, -65.35 >>
sPickupData[8].vRot = << 174.15, -58.20, -144.66 >>
sPickupData[9].vRot = << -28.22, 36.11, -90.22 >>
sPickupData[10].vRot = << -5.00, 44.00, 65.00 >>
sPickupData[11].vRot = << 0.00, -108.00, 45.36 >>
sPickupData[12].vRot = << -10.90, 43.12, 131.52 >>
sPickupData[13].vRot = << 12.37, 49.66, 126.88 >>
sPickupData[14].vRot = << 4.35, 10.22, 27.51 >>
sPickupData[15].vRot = << 0.00, 10.00, 60.73 >>
sPickupData[16].vRot = << 150.56, -78.67, -174.35 >>
sPickupData[17].vRot = << -0.00, 50.00, 46.41 >>
sPickupData[18].vRot = << 6.26, 44.65, 8.88 >>
sPickupData[19].vRot = << 90.00, 78.50, 16.04 >>
sPickupData[20].vRot = << -1.36, 6.53, -70.94 >>
sPickupData[21].vRot = << -61.86, 78.61, 126.50 >>
sPickupData[22].vRot = << -10.00, -80.71, 69.70 >>
sPickupData[23].vRot = << -76.43, -70.02, 29.74 >>
sPickupData[24].vRot = << -28.00, -70.00, 107.53 >>
sPickupData[25].vRot = << 4.19, 6.46, 110.43 >>
sPickupData[26].vRot = << -3.19, 60.60, 25.00 >>
sPickupData[27].vRot = << 0.00, -81.68, -20.16 >>
sPickupData[28].vRot = << 7.39, 44.52, 19.65 >>
sPickupData[29].vRot = << 89.74, 80.95, 151.74 >>
ENDPROC
/// PURPOSE:
/// Updates the diving pickups, returns TRUE when they have all been collected
FUNC BOOL UPDATE_DIVING_PICKUPS(SCRAP_MISSION_DATA &missionData, SCRAP_PICKUP_DATA &pickupData[])
INT i
BOOL bScrapCollected
VECTOR vPlayerPos
// cache the player position and do is injured check to stop asserts
IS_PED_INJURED(PLAYER_PED_ID())
vPlayerPos = GET_ENTITY_COORDS(PLAYER_PED_ID())
REPEAT SCRAPS_TO_CHECK_PER_FRAME i
// Has this scrap already been collected?
bScrapCollected = HAS_SCRAP_BEEN_COLLECTED(missionData.scrapData, missionData.iCurrentCheckIndex)
// If the scrap isn't active...
IF NOT pickupData[missionData.iCurrentCheckIndex].bActive
// ...and hasn't been collected
IF NOT bScrapCollected
IF NOT ARE_VECTORS_EQUAL(pickupData[missionData.iCurrentCheckIndex].vRot, <<0,0,0>>)
CREATE_SCRAP_WHEN_IN_RANGE(pickupData[missionData.iCurrentCheckIndex], missionData.packageMdl, PICKUP_CUSTOM_SCRIPT, FALSE, TRUE, EULER_XYZ)
ELSE
CREATE_SCRAP_WHEN_IN_RANGE(pickupData[missionData.iCurrentCheckIndex], missionData.packageMdl)
ENDIF
ENDIF
ELSE
// Update pickup state
IF NOT bScrapCollected
UPDATE_SCRAP_PICKUP(missionData, pickupData, vPlayerPos)
ENDIF
ENDIF
// Increase the current index - make sure it doesn't overrun
missionData.iCurrentCheckIndex++
IF (missionData.iCurrentCheckIndex >= COUNT_OF(pickupData))
missionData.iCurrentCheckIndex = 0
ENDIF
ENDREPEAT
// We need to display that a pickup has been collected
IF missionData.bDisplayMessage
// Display collection message
CDEBUG1LN(debug_ambient,"Displaying ambient diving message")
IF NOT g_bResultScreenDisplaying
UPDATE_DISPLAY_MESSAGE(missionData.bDisplayMessage, missionData.bMessageOnDisplay, missionData.iMessageTimer, SCRAP_DIVING, iMessageStage, siMessage, "DIVING_TITLE", "DIVING_COLLECT")
ELSE
CDEBUG1LN(debug_ambient,"Can't display diving message, results screen is currently active")
ENDIF
// Check for pickups in the vicinity
// Return to a Dinghy to continue hunting for the submarine pieces.
IF NOT HAS_ONE_TIME_HELP_DISPLAYED(FHM_DINGHY_HELP4)
// Are there any remaining
VECTOR vPos = GET_ENTITY_COORDS(PLAYER_PED_ID())
INT iNearestPickup
IF GET_NEAREST_PICKUP_INDEX(sMissionData, sPickupData, vPos, iNearestPickup)
IF GET_DISTANCE_BETWEEN_COORDS(GET_DIVING_COORD(iNearestPickup), vPos, FALSE) > BLIP_WRECK_DISTANCE
ADD_HELP_TO_FLOW_QUEUE("DIVING_HELP4", FHP_MEDIUM, DEFAULT_GOD_TEXT_TIME)
SET_ONE_TIME_HELP_MESSAGE_DISPLAYED(FHM_DINGHY_HELP4)
ENDIF
ENDIF
ENDIF
ENDIF
IF NOT bCallRegistered
IF missionData.scrapData.iScrapsCollected >= missionData.scrapData.iMaxScraps
bCallRegistered = REGISTER_CALL_FROM_CHARACTER_TO_PLAYER(CALL_SONAR_COLLECT_DONE, CT_FLOW, BIT_MICHAEL, CHAR_ABIGAIL, 3, 1000, 10000, TEXT_ABIGAIL_MISSED, VID_BLANK, CID_ABIGAIL1_FOUND_SUB_WRECKAGE)
ENDIF
ENDIF
// Send the complete flag once everything is collected and message has finished
IF NOT missionData.bMessageOnDisplay
AND NOT missionData.bDisplayMessage
RETURN (missionData.scrapData.iScrapsCollected >= missionData.scrapData.iMaxScraps)
ENDIF
RETURN FALSE
ENDFUNC
//----------------------
// DEBUG FUNCTIONS
//----------------------
#IF IS_DEBUG_BUILD
/// PURPOSE:
/// Setup Debug Widgets
PROC SETUP_DEBUG_WIDGETS(SCRAP_MISSION_DATA &missionData, SCRAP_PICKUP_DATA &pickupData[])
INT iScrap
TEXT_LABEL_63 tlTemp
mWidgetGroup = START_WIDGET_GROUP("Ambient: Submarine Wreckage")
ADD_WIDGET_BOOL("Show all scraps on map", bMapAllScraps)
ADD_WIDGET_BOOL("Force Quit Script", bDebugQuitScript)
ADD_WIDGET_BOOL("Allow debug warp", bDebugAllowWarping)
ADD_WIDGET_BOOL("Allow debug tty", bShowScrapDebugTTY)
START_WIDGET_GROUP("Collection")
ADD_WIDGET_BOOL("Collect all scraps", bCollectAllScraps)
ADD_WIDGET_BOOL("Reset scraps collection", bResetScrapCollection)
ADD_WIDGET_BOOL("Simulate Scrap Collect", bSimulateScrapCollect)
ADD_WIDGET_INT_SLIDER("Sim Scraps to Collect", iSimulateScrapCollect, 1, COUNT_OF(pickupData), 1)
STOP_WIDGET_GROUP()
START_WIDGET_GROUP("Stats")
ADD_WIDGET_INT_READ_ONLY("Scraps Collected", missionData.scrapData.iScrapsCollected)
ADD_WIDGET_INT_READ_ONLY("Total Scraps", missionData.scrapData.iMaxScraps)
STOP_WIDGET_GROUP()
START_WIDGET_GROUP("Positions")
REPEAT COUNT_OF(pickupData) iScrap
tlTemp = "Scrap "
tlTemp += iScrap
START_WIDGET_GROUP(tlTemp)
ADD_WIDGET_BOOL("bWarp", bWarpToScrap[iScrap])
ADD_WIDGET_FLOAT_READ_ONLY("X Position", pickupData[iScrap].vCoords.x)
ADD_WIDGET_FLOAT_READ_ONLY("Y Position", pickupData[iScrap].vCoords.y)
ADD_WIDGET_FLOAT_READ_ONLY("Z Position", pickupData[iScrap].vCoords.z)
STOP_WIDGET_GROUP()
ENDREPEAT
STOP_WIDGET_GROUP()
STOP_WIDGET_GROUP()
ENDPROC
/// PURPOSE:
/// Updates all The Widgets
PROC UPDATE_DEBUG_WIDGETS(SCRAP_MISSION_DATA &missionData, SCRAP_PICKUP_DATA &pickupData[])
INT i
INT cnt = 0
IF (bResetScrapCollection)
CPRINTLN(DEBUG_AMBIENT, "Resetting Scrap Collection")
STAT_SET_INT(missionData.packageStat, 0)
REPEAT COUNT_OF(pickupData) i
SAFE_REMOVE_PICKUP(pickupData[i].pickup)
SAFE_REMOVE_BLIP(pickupData[i].blip)
SET_SCRAP_AS_COLLECTED(missionData.scrapData, i, FALSE)
UPDATE_GLOBAL_SCRAP_DATA(missionData.missionType, i, FALSE)
pickupData[i].pickup = NULL
pickupData[i].bActive = FALSE
ENDREPEAT
SET_PACKED_STATS_FROM_SCRAP_COLLECT_DATA(missionData.scrapData)
bResetScrapCollection = FALSE
bMapAllScraps = TRUE
SET_AUTOSAVE_IGNORES_ON_MISSION_FLAG(TRUE)
MAKE_AUTOSAVE_REQUEST()
ENDIF
IF (bSimulateScrapCollect)
CPRINTLN(DEBUG_AMBIENT, "Simulating Collecting ", iSimulateScrapCollect, "Scraps")
REPEAT COUNT_OF(pickupData) i
IF NOT HAS_SCRAP_BEEN_COLLECTED(missionData.scrapData, i)
COLLECT_SCRAP(missionData, pickupData, i)
cnt++
ENDIF
IF (cnt >= iSimulateScrapCollect)
i = COUNT_OF(pickupData) + 1 // so we break out of array
ENDIF
ENDREPEAT
bMapAllScraps = TRUE
bSimulateScrapCollect = FALSE
SET_AUTOSAVE_IGNORES_ON_MISSION_FLAG(TRUE)
MAKE_AUTOSAVE_REQUEST()
ENDIF
IF (bCollectAllScraps)
CPRINTLN(DEBUG_AMBIENT, "Collecting All Scraps")
STAT_SET_INT(missionData.packageStat, 0)
REPEAT COUNT_OF(pickupData) i
COLLECT_SCRAP(missionData, pickupData, i)
ENDREPEAT
bCollectAllScraps = FALSE
bMapAllScraps = TRUE
SET_AUTOSAVE_IGNORES_ON_MISSION_FLAG(TRUE)
MAKE_AUTOSAVE_REQUEST()
ENDIF
// Blip handler
IF (bMapAllScraps)
IF NOT bScrapsAreBlipped
BLIP_SCRAP_POSITIONS(missionData, pickupData)
bScrapsAreBlipped = TRUE
ENDIF
ELSE
IF bScrapsAreBlipped
BLIP_SCRAP_POSITIONS(missionData, pickupData, FALSE)
bScrapsAreBlipped = FALSE
ENDIF
ENDIF
// Handle warping
IF (bDebugAllowWarping = FALSE)
EXIT
ENDIF
REPEAT COUNT_OF(pickupData) i
IF (bWarpToScrap[i])
CPRINTLN(DEBUG_AMBIENT, "Warping To Scrap:", i)
LOAD_SCENE(pickupData[i].vCoords)
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
SET_ENTITY_COORDS(PLAYER_PED_ID(), pickupData[i].vCoords+<<0.0, -2.5, 0.0>>)
FORCE_PED_MOTION_STATE(PLAYER_PED_ID(), MS_DIVING_IDLE)
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
SET_ENTITY_HEADING(PLAYER_PED_ID(), 0.0)
ENDIF
ENDIF
bWarpToScrap[i] = FALSE
ENDIF
ENDREPEAT
ENDPROC
/// PURPOSE:
/// Cleans up all the widgets
PROC CLEANUP_DEBUG_WIDGETS()
IF DOES_WIDGET_GROUP_EXIST(mWidgetGroup)
DELETE_WIDGET_GROUP(mWidgetGroup)
ENDIF
ENDPROC
#ENDIF
/// PURPOSE:
/// Player is a dinghy vehicle
FUNC BOOL IS_PLAYER_IN_DINGHY()
IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID())
VEHICLE_INDEX vehIndex = GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID())
IF DOES_ENTITY_EXIST(vehIndex)
IF GET_ENTITY_MODEL(vehIndex) = DINGHY
OR GET_ENTITY_MODEL(vehIndex) = DINGHY2
RETURN TRUE
ELSE
RETURN FALSE
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Checks whether the player is currently in the dinghy associated with this mission script
FUNC BOOL IS_PLAYER_IN_MISSION_DINGHY()
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
// Check for player remaining in current boat
IF IS_ENTITY_ALIVE(viCurrentDinghy)
IF IS_PED_IN_VEHICLE(PLAYER_PED_ID(), viCurrentDinghy)
RETURN TRUE
ENDIF
ELSE
// Check for player getting into a new vehicle
IF IS_PLAYER_IN_DINGHY()
// Get handle
viCurrentDinghy = GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID())
IF DOES_ENTITY_EXIST(viCurrentDinghy)
// Take ownership of boat
SET_ENTITY_AS_MISSION_ENTITY(viCurrentDinghy, TRUE, TRUE)
// Needed so we can check for anchoring when exiting the boat
SET_ENTITY_RECORDS_COLLISIONS(viCurrentDinghy, TRUE)
RETURN TRUE
ENDIF
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Removes boat blip
PROC REMOVE_DINGHY_BLIP()
IF DOES_BLIP_EXIST(biDinghy)
CPRINTLN(DEBUG_AMBIENT, "Ambient Diving: REMOVE_DINGHY_BLIP()")
REMOVE_BLIP(biDinghy)
ENDIF
ENDPROC
/// PURPOSE:
/// Remove boat blip, unanchor and release boat (if it exists)
PROC RELEASE_DINGHY()
// Remove blip
REMOVE_DINGHY_BLIP()
// Release boat
IF IS_ENTITY_ALIVE(viCurrentDinghy)
CPRINTLN(DEBUG_AMBIENT, "Ambient Diving: RELEASE_DINGHY()")
IF IS_THIS_MODEL_A_BOAT(GET_ENTITY_MODEL(viCurrentDinghy))
SET_BOAT_ANCHOR(viCurrentDinghy, FALSE)
ENDIF
// No longer need to monitor for anchoring purposes
SET_ENTITY_RECORDS_COLLISIONS(viCurrentDinghy, FALSE)
// Release boat reference if created by this script
IF IS_ENTITY_A_MISSION_ENTITY(viCurrentDinghy)
SET_VEHICLE_AS_NO_LONGER_NEEDED(viCurrentDinghy)
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Initialise everything
PROC DO_INITALIZE()
iMessageStage = 0
IF IS_PLAYER_IN_MISSION_DINGHY()
iDinghyStatus = DS_IN_DINGHY
ELSE
iDinghyStatus = DS_NOT_IN_DINGHY
ENDIF
// Setup pickup positions and initialise mission details and stats
INITIALISE_DIVING_SCRAPS()
SETUP_SCRAP_MISSION(sMissionData, SCRAP_DIVING, PROP_SUB_CHUNK_01, "DIVING_COLLECT")
SETUP_SCRAP_MISSION_STATS(sMissionData, NUM_HIDDEN_PACKAGES_4, PACKSTAT_DIVING_START, NUMBER_OF_DIVING_SCRAPS)
SET_STAGE(MS_COLLECT_PICKUPS)
fExtraBlipSize[0] = 0.25
fExtraBlipSize[1] = 0.5
fExtraBlipSize[2] = 0.75
fExtraBlipSize[3] = 1.0
#IF IS_DEBUG_BUILD
ADD_CONTACT_TO_PHONEBOOK(CHAR_ABIGAIL, MICHAEL_BOOK, FALSE)
#ENDIF
ENDPROC
/// PURPOSE:
/// Handles blipping and anchoring the player's last boat
PROC UPDATE_DINGHY()
SWITCH iDinghyStatus
CASE DS_NOT_IN_DINGHY
IF IS_PLAYER_IN_MISSION_DINGHY()
IF IS_ENTITY_ALIVE(viCurrentDinghy)
// Unblip dinghy
REMOVE_DINGHY_BLIP()
// Allow dinghy to move away again...
SET_BOAT_ANCHOR(viCurrentDinghy, FALSE)
CPRINTLN(DEBUG_AMBIENT, "Ambient Diving: Dinghy status = DS_IN_DINGHY")
iDinghyStatus = DS_IN_DINGHY
// Complete script when back in the boat after collecting all pickups
IF eStage = MS_POST_COMPLETION
CPRINTLN(DEBUG_AMBIENT, "Ambient Diving: MS_POST_COMPLETION->MS_CLEANUP")
SET_STAGE(MS_CLEANUP)
ENDIF
ENDIF
ELSE
// Detect the player going far away from the dinghy
IF IS_ENTITY_ALIVE(viCurrentDinghy) AND IS_ENTITY_ALIVE(PLAYER_PED_ID())
FLOAT fCleanupDist
// Extend range if the player is swimming
IF IS_ENTITY_IN_WATER(PLAYER_PED_ID())
fCleanupDist = DINGHY_CLEANUP_RANGE_IN_WATER
ELSE
fCleanupDist = DINGHY_CLEANUP_RANGE
ENDIF
// Check range
IF GET_DISTANCE_BETWEEN_ENTITIES(PLAYER_PED_ID(), viCurrentDinghy, FALSE) > fCleanupDist
CPRINTLN(DEBUG_AMBIENT, "Ambient Diving: Dinghy status = OUT OF RANGE!")
CPRINTLN(DEBUG_AMBIENT, "Ambient Diving: Cleanup distance = ", fCleanupDist)
CPRINTLN(DEBUG_AMBIENT, "Ambient Diving: Distance between player and mission dinghy = ", GET_DISTANCE_BETWEEN_ENTITIES(PLAYER_PED_ID(), viCurrentDinghy, FALSE))
RELEASE_DINGHY()
// Complete script as moved away from the dinghy
IF eStage = MS_POST_COMPLETION
CPRINTLN(DEBUG_AMBIENT, "Ambient Diving: MS_POST_COMPLETION->MS_CLEANUP")
SET_STAGE(MS_CLEANUP)
ENDIF
ENDIF
ELSE
// Remove blip when destroyed
REMOVE_DINGHY_BLIP()
// Complete script as dinghy is no longer available
IF eStage = MS_POST_COMPLETION
CPRINTLN(DEBUG_AMBIENT, "Ambient Diving: Dinghy destroyed...")
CPRINTLN(DEBUG_AMBIENT, "Ambient Diving: MS_POST_COMPLETION->MS_CLEANUP")
SET_STAGE(MS_CLEANUP)
ENDIF
ENDIF
ENDIF
BREAK
CASE DS_IN_DINGHY
// Complete script when back in the boat after collecting all pickups
IF eStage = MS_POST_COMPLETION
CPRINTLN(DEBUG_AMBIENT, "Ambient Diving: Player in dinghy...")
CPRINTLN(DEBUG_AMBIENT, "Ambient Diving: MS_POST_COMPLETION->MS_CLEANUP")
SET_STAGE(MS_CLEANUP)
ENDIF
IF NOT IS_PLAYER_IN_MISSION_DINGHY()
IF IS_ENTITY_ALIVE(viCurrentDinghy)
// Blip the dinghy!
IF NOT DOES_BLIP_EXIST(biDinghy)
CPRINTLN(DEBUG_AMBIENT, "Ambient Diving: Creating blip for dinghy...")
biDinghy = CREATE_BLIP_FOR_VEHICLE(viCurrentDinghy)
ENDIF
SET_VEHICLE_ENGINE_ON(viCurrentDinghy, FALSE, TRUE) // B*1789427 Ensure the engine is turned off
// Set anchor so it doesn't float away!
IF IS_THIS_MODEL_A_BOAT(GET_ENTITY_MODEL(viCurrentDinghy))
IF NOT HAS_ENTITY_COLLIDED_WITH_ANYTHING(viCurrentDinghy)
CPRINTLN(DEBUG_AMBIENT, "Ambient Diving: Anchoring last player dinghy.")
SET_BOAT_ANCHOR(viCurrentDinghy, TRUE) // B*1789427 Ensure the dinghy gets anchored
ELSE
CPRINTLN(DEBUG_AMBIENT, "Ambient Diving: Unable to anchor last player dinghy as it is colliding with something.")
ENDIF
ENDIF
CPRINTLN(DEBUG_AMBIENT, "Ambient Diving: Dinghy status = DS_NOT_IN_DINGHY")
iDinghyStatus = DS_NOT_IN_DINGHY
ENDIF
ENDIF
BREAK
ENDSWITCH
ENDPROC
/// PURPOSE:
/// Helper function to remove sonar blip and outline
PROC REMOVE_CURRENT_SONAR_BLIP()
// Main blip
IF DOES_BLIP_EXIST(biSonarBlip)
REMOVE_BLIP(biSonarBlip)
ENDIF
// Thickenened outline
INT i
FOR i=0 TO 3
IF DOES_BLIP_EXIST(biSonarOutline[i])
REMOVE_BLIP(biSonarOutline[i])
ENDIF
ENDFOR
ENDPROC
/// PURPOSE:
/// Update wreckage area blip when far away and sonar blips when nearby
PROC UPDATE_SONAR_BLIPS()
// Get current player location
VECTOR vPlayerPos = GET_ENTITY_COORDS(PLAYER_PED_ID())
// Work out the nearest submarine piece
IF GET_NEAREST_PICKUP_INDEX(sMissionData, sPickupData, vPlayerPos, iNearestScrap)
VECTOR vScrapPos = GET_DIVING_COORD(iNearestScrap)
FLOAT fDistance = GET_DISTANCE_BETWEEN_COORDS(vScrapPos, vPlayerPos, FALSE)
// Player is some distance away from the nearest wreck
IF fDistance > BLIP_WRECK_DISTANCE
// Remove close range blip
REMOVE_CURRENT_SONAR_BLIP()
// Ensure sonar is refreshed correctly when moving in range again
IF NOT bSonarUpdate
bSonarUpdate = TRUE
ENDIF
// Only update when player is in the dinghy
IF IS_PLAYER_IN_DINGHY()
// The nearest location where a submarine piece can be found is marked on the map when piloting the Dinghy.
IF NOT HAS_ONE_TIME_HELP_DISPLAYED(FHM_DINGHY_HELP1)
ADD_HELP_TO_FLOW_QUEUE("DIVING_HELP1", FHP_MEDIUM)
SET_ONE_TIME_HELP_MESSAGE_DISPLAYED(FHM_DINGHY_HELP1)
ENDIF
// Force a blip update if the nearest position has changed
IF iNearestScrap <> iBlippedSite
IF DOES_BLIP_EXIST(biWreckSite)
REMOVE_BLIP(biWreckSite)
ENDIF
ENDIF
// Area blip for closest wreck
IF NOT DOES_BLIP_EXIST(biWreckSite)
CPRINTLN(DEBUG_AMBIENT, "Ambient Diving: Creating wreckage site blip...")
biWreckSite = ADD_BLIP_FOR_RADIUS(vScrapPos, 200)
SET_BLIP_ALPHA(biWreckSite, 128)
SET_BLIP_COLOUR(biWreckSite, BLIP_COLOUR_GREEN)
SET_BLIP_HIDDEN_ON_LEGEND(biWreckSite, FALSE)
SET_BLIP_NAME_FROM_TEXT_FILE(biWreckSite, "B_WRE")
SHOW_HEIGHT_ON_BLIP(biWreckSite, FALSE)
// Store which site is blipped
iBlippedSite = iNearestScrap
CPRINTLN(DEBUG_AMBIENT, "Ambient Diving: Blipped site at location ", iBlippedSite)
ENDIF
ELSE
// Remove wreckage area blip in the event we have moved within range
IF DOES_BLIP_EXIST(biWreckSite)
REMOVE_BLIP(biWreckSite)
ENDIF
ENDIF
ELSE
// Use the Dinghy's sonar to help locate the missing submarine pieces.
IF IS_PLAYER_IN_DINGHY()
IF NOT HAS_ONE_TIME_HELP_DISPLAYED(FHM_DINGHY_HELP2)
ADD_HELP_TO_FLOW_QUEUE("DIVING_HELP2", FHP_MEDIUM)
SET_ONE_TIME_HELP_MESSAGE_DISPLAYED(FHM_DINGHY_HELP2)
ENDIF
ENDIF
// Dive underwater to search for the submarine pieces.
IF HAS_ONE_TIME_HELP_DISPLAYED(FHM_DINGHY_HELP2)
IF NOT HAS_ONE_TIME_HELP_DISPLAYED(FHM_DINGHY_HELP3)
IF IS_ENTITY_IN_WATER(PLAYER_PED_ID())
AND NOT IS_PED_SWIMMING_UNDER_WATER(PLAYER_PED_ID())
ADD_HELP_TO_FLOW_QUEUE("DIVING_HELP3", FHP_MEDIUM, 2000)
SET_ONE_TIME_HELP_MESSAGE_DISPLAYED(FHM_DINGHY_HELP3)
ENDIF
ENDIF
ENDIF
// Remove area blip
IF DOES_BLIP_EXIST(biWreckSite)
CPRINTLN(DEBUG_AMBIENT, "Ambient Diving: Within range - removing wreckage site blip...")
REMOVE_BLIP(biWreckSite)
ENDIF
IF iNearestScrap <> iBlippedSite
REMOVE_CURRENT_SONAR_BLIP()
iBlippedSite = iNearestScrap
bSonarUpdate = TRUE
CPRINTLN(DEBUG_AMBIENT, "Ambient Diving: Blipped site updated to location ", iBlippedSite)
ENDIF
// We haven't yet collected all submarine pieces
IF (sMissionData.scrapData.iScrapsCollected < sMissionData.scrapData.iMaxScraps)
// Trigger sonar update
IF bSonarUpdate
// Initialise sonar blip
fCurrentRadius = 0
fTargetRadius = fDistance
// Set minimum size based on whether player is in the dinghy or swimming
IF IS_PLAYER_IN_DINGHY()
fTargetRadius = CLAMP(fTargetRadius, SONAR_MIN_SIZE_BOAT, BLIP_WRECK_DISTANCE)
ELSE
fTargetRadius = CLAMP(fTargetRadius, SONAR_MIN_SIZE_SWIM, BLIP_WRECK_DISTANCE)
ENDIF
// Set update rate
fSonarIncrement = fTargetRadius*0.025
bSonarUpdate = FALSE
// Trigger sound from wreckage piece location
IF IS_PLAYER_IN_DINGHY()
PLAY_SOUND_FROM_COORD(iSonarID, "SINGLE_BLIP_FROM_BOAT", vScrapPos, "ABIGAIL_SONAR_SOUNDSET")
ELSE
PLAY_SOUND_FROM_COORD(iSonarID, "SCRIPT_TRIGGERED_FROM_PROP", vPlayerPos, "ABIGAIL_SONAR_SOUNDSET")
ENDIF
ELSE
// Update size
fCurrentRadius += fSonarIncrement
fCurrentRadius = CLAMP(fCurrentRadius, 0, fTargetRadius)
IF NOT DOES_BLIP_EXIST(biSonarBlip)
biSonarBlip = ADD_BLIP_FOR_RADIUS(vScrapPos, fCurrentRadius)
SET_BLIP_ALPHA(biSonarBlip, 255)
SET_BLIP_COLOUR(biSonarBlip, BLIP_COLOUR_WHITE)
SET_BLIP_AS_SHORT_RANGE(biSonarBlip, TRUE)
SET_RADIUS_BLIP_EDGE(biSonarBlip, TRUE)
SHOW_HEIGHT_ON_BLIP(biSonarBlip, FALSE)
ELSE
SET_BLIP_SCALE(biSonarBlip, fCurrentRadius)
ENDIF
INT i = 0
IF IS_PLAYER_IN_DINGHY()
FOR i=0 TO 3
IF NOT DOES_BLIP_EXIST(biSonarOutline[i])
biSonarOutline[i] = ADD_BLIP_FOR_RADIUS(vScrapPos, fCurrentRadius + fExtraBlipSize[i])
SET_BLIP_ALPHA(biSonarOutline[i], 255)
SET_BLIP_COLOUR(biSonarOutline[i], BLIP_COLOUR_WHITE)
SET_BLIP_AS_SHORT_RANGE(biSonarOutline[i], TRUE)
SET_RADIUS_BLIP_EDGE(biSonarOutline[i], TRUE)
SHOW_HEIGHT_ON_BLIP(biSonarOutline[i], FALSE)
ELSE
SET_BLIP_SCALE(biSonarOutline[i], fCurrentRadius + fExtraBlipSize[i])
ENDIF
ENDFOR
ELSE
FOR i=0 TO 3
IF DOES_BLIP_EXIST(biSonarOutline[i])
SET_BLIP_SCALE(biSonarOutline[i], fCurrentRadius)
ENDIF
ENDFOR
ENDIF
// We need to create a new blip
IF fCurrentRadius >= fTargetRadius
bSonarUpdate = TRUE
ENDIF
ENDIF
ELSE
// Collected all submarine wreckage pieces - make sure to remove last sonar blip
REMOVE_CURRENT_SONAR_BLIP()
ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// When all scraps are collected, advance to the next stage
PROC UPDATE_PICKUPS()
// Has the player collected all of the diving scraps?
IF UPDATE_DIVING_PICKUPS(sMissionData, sPickupData)
OR GET_MISSION_FLOW_FLAG_STATE(FLOWFLAG_DIVING_SCRAPS_DONE) = TRUE
// Disable trackify
IF IS_CELLPHONE_TRACKIFY_IN_USE()
ENABLE_SECOND_SCREEN_TRACKIFY_APP(FALSE)
ENDIF
// Completion percentage
CPRINTLN(DEBUG_AMBIENT, "All scraps have been collected")
REGISTER_SCRIPT_IN_COMPLETION_PERCENTAGE_TOTAL (CP_DIVCOLL)
// Register phonecall
IF NOT GET_MISSION_FLOW_FLAG_STATE(FLOWFLAG_DIVING_SCRAPS_DONE)
IF NOT bCallRegistered
bCallRegistered = REGISTER_CALL_FROM_CHARACTER_TO_PLAYER(CALL_SONAR_COLLECT_DONE, CT_FLOW, BIT_MICHAEL, CHAR_ABIGAIL, 3, 1000, 10000, TEXT_ABIGAIL_MISSED, VID_BLANK, CID_ABIGAIL1_FOUND_SUB_WRECKAGE)
ENDIF
ENDIF
// Monitor player returning to the boat or moving out of range
SET_STAGE(MS_POST_COMPLETION)
ENDIF
ENDPROC
/// PURPOSE:
/// Script Cleanup
PROC SCRIPT_CLEANUP( BOOL bTerminate = TRUE )
CPRINTLN(DEBUG_MISSION, "Cleanup diving Scrap Collection")
#IF IS_DEBUG_BUILD
CLEANUP_DEBUG_WIDGETS()
#ENDIF
// Remove area blip
IF DOES_BLIP_EXIST(biWreckSite)
REMOVE_BLIP(biWreckSite)
ENDIF
// Remove sonar blip
REMOVE_CURRENT_SONAR_BLIP()
// Remove sound
IF NOT HAS_SOUND_FINISHED(iSonarID)
STOP_SOUND(iSonarID)
ENDIF
// Cleanup boat
RELEASE_DINGHY()
// Cleanup existing pickups
INT i
REPEAT COUNT_OF(sPickupData) i
SAFE_REMOVE_BLIP(sPickupData[i].blip)
SAFE_REMOVE_PICKUP(sPickupData[i].pickup)
sPickupData[i].pickup = NULL
ENDREPEAT
IF( bTerminate )
// Have we collected all diving pickups?
IF HAVE_ALL_SCRAPS_BEEN_COLLECTED(sMissionData.scrapData)
OR GET_MISSION_FLOW_FLAG_STATE(FLOWFLAG_DIVING_SCRAPS_DONE) = TRUE
// No longer require the script to be relaunched when loading from a savegame.
Remove_Script_From_Relaunch_List(LAUNCH_BIT_AMB_DIVING_PICKUPS)
ENDIF
SET_MODEL_AS_NO_LONGER_NEEDED(sMissionData.packageMdl)
TERMINATE_THIS_THREAD()
ELSE
CPRINTLN(DEBUG_AMBIENT, "SCRIPT_CLEANUP - Lite cleanup done")
eStage = MS_INIT
ENDIF
ENDPROC
//----------------------
// MAIN SCRIPT
//----------------------
SCRIPT
// Setup callbacks
IF HAS_FORCE_CLEANUP_OCCURRED(FORCE_CLEANUP_FLAG_DEBUG_MENU|FORCE_CLEANUP_FLAG_SP_TO_MP|FORCE_CLEANUP_FLAG_REPEAT_PLAY|FORCE_CLEANUP_FLAG_DIRECTOR)
SCRIPT_CLEANUP()
ENDIF
// Close down in the event that this script is already running
IF GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH (HASH("ambient_Diving")) > 1
CPRINTLN(DEBUG_AMBIENT, "diving is attempting to launch with an instance already active.")
TERMINATE_THIS_THREAD()
ENDIF
// Register the script so that it can be relaunched when loading from a savegame.
Register_Script_To_Relaunch_List(LAUNCH_BIT_AMB_DIVING_PICKUPS)
// Setup arrays and everything else
CPRINTLN(DEBUG_AMBIENT, "Initializing Diving Scrap Collection - Version:", XVERSION_NUMBER)
// Setup debug widgets
#IF IS_DEBUG_BUILD
SETUP_DEBUG_WIDGETS(sMissionData, sPickupData)
#ENDIF
// Main loop
WHILE (TRUE)
WAIT(0)
IF( IS_CURRENTLY_ON_MISSION_TO_TYPE( MISSION_TYPE_STORY ) )
IF( NOT bOnMissionCleanupDone )
SCRIPT_CLEANUP( FALSE )
bOnMissionCleanupDone = TRUE
ENDIF
ELSE
bOnMissionCleanupDone = FALSE
IS_ENTITY_OK(PLAYER_PED_ID())
SWITCH eStage
CASE MS_INIT
DO_INITALIZE()
BREAK
CASE MS_COLLECT_PICKUPS
UPDATE_SONAR_BLIPS()
UPDATE_DINGHY()
UPDATE_PICKUPS()
BREAK
CASE MS_POST_COMPLETION
UPDATE_DINGHY()
BREAK
CASE MS_CLEANUP
SCRIPT_CLEANUP()
BREAK
ENDSWITCH
// Debug widgets
#IF IS_DEBUG_BUILD
UPDATE_DEBUG_WIDGETS(sMissionData, sPickupData)
IF bDebugQuitScript
SCRIPT_CLEANUP()
ENDIF
#ENDIF
ENDIF
ENDWHILE
ENDSCRIPT