////////////////////////////////////////////////////////////////////////////////////////// // // // SCRIPT NAME : common_packages.sc // // AUTHOR : Joanna Wright // // DESCRIPTION : common functionality for spaceship and letter scraps // // // // // ////////////////////////////////////////////////////////////////////////////////////////// USING "RC_Helper_Functions.sch" // Pickup spawn and cleanup distances CONST_FLOAT CREATE_PICKUP_DISTANCE 50.0 CONST_FLOAT CREATE_SALE_SIGN_DISTANCE 100.0 CONST_FLOAT CLEANUP_PICKUP_DISTANCE 60.00 CONST_FLOAT CLEANUP_OBJECT_DISTANCE 110.0 // Rumble effect CONST_INT PICKUP_RUMBLE_DURATION 200 CONST_INT PICKUP_RUMBLE_FREQ 250 // Packed stats CONST_INT PACKSTAT_LETTER_START PACKED_HIDDEN_PACKAGE_START CONST_INT PACKSTAT_UFO_START PACKSTAT_LETTER_START+50 CONST_INT PACKSTAT_TRACT_START PACKSTAT_LETTER_START+100 CONST_INT PACKSTAT_SONAR_START PACKSTAT_LETTER_START+110 CONST_INT PACKSTAT_DIVING_START PACKSTAT_LETTER_START+140 // Pickup constants (spaceship and letter scraps are globally stored) CONST_INT MAX_TRACT_PICKUPS 10 CONST_INT NUMBER_OF_SONAR_PICKUPS 30 CONST_INT NUMBER_OF_FOR_SALE_SIGNS 15 CONST_INT NUMBER_OF_DIVING_SCRAPS 30 // Mission enum ENUM SCRAP_MISSION SCRAP_SHIP, SCRAP_LETTER, SCRAP_SALE_SIGNS, SCRAP_SUB, SCRAP_TRACT, SCRAP_DIVING, SCRAP_PEYOTE, SCRAP_WILDLIFE, SCRAP_MONKEY ENDENUM // Ambient script stage ENUM MISSION_STAGE STAGE_INITIALISE = 0, STAGE_PROCESS, STAGE_CLEANUP ENDENUM // Debug #IF IS_DEBUG_BUILD BOOL bDebug_PrintToTTYStuff = FALSE // This seems to be off for the For Sale signs INT iDebugScrapCreatedNum = 0 // End up with negative numbers #ENDIF // Hidden package instance STRUCT SCRAP_PICKUP_DATA OBJECT_INDEX obj PICKUP_INDEX pickup BLIP_INDEX blip VECTOR vCoords VECTOR vRot FLOAT fHeading BOOL bActive ENDSTRUCT //Returns the correct bit flag slot depending on what object part we are looking for. only to be used if scrap has over 1 flag FUNC INT RETURN_BIT_FLAG_SLOT(INT iScrapParts, INT objectPartNum) IF objectPartNum < 32 RETURN objectPartNum ELSE RETURN (iScrapParts - objectPartNum) ENDIF ENDFUNC /// PURPOSE: // Loads any interior around the scrap specified /// PARAMS: /// VECTOR coords = the coord of the pickup or scrap /// RETURNS: /// N/A PROC LOAD_INTERIOR_AROUND_COORDS(VECTOR coords) INTERIOR_INSTANCE_INDEX intIndex = GET_INTERIOR_AT_COORDS(coords) IF IS_VALID_INTERIOR(intIndex) PIN_INTERIOR_IN_MEMORY(intIndex) WHILE NOT IS_INTERIOR_READY(intIndex) WAIT(0) ENDWHILE WAIT(0) UNPIN_INTERIOR(intIndex) ENDIF ENDPROC /// PURPOSE: // When the player is close to the pickup location, create the scrap pickup PROC CREATE_SCRAP_WHEN_IN_RANGE(SCRAP_PICKUP_DATA& theScrap, MODEL_NAMES objModel, PICKUP_TYPE pickupType = PICKUP_CUSTOM_SCRIPT, BOOL bIsInInterior = FALSE, BOOL bUseRotate=FALSE, EULER_ROT_ORDER rotOrder=EULER_YXZ) INT iPlacementFlags IF NOT theScrap.bActive // If the player is close to the scrap location IF NOT IS_PED_INJURED(PLAYER_PED_ID()) //Don't allow creation of pickups while playing as a none story character. IF NOT IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_ANIMAL) AND NOT IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_DIRECTOR) IF NOT DOES_PICKUP_EXIST(theScrap.pickup) IF (VDIST2(GET_ENTITY_COORDS(PLAYER_PED_ID()), theScrap.vCoords) <= (CREATE_PICKUP_DISTANCE*CREATE_PICKUP_DISTANCE)) // Request pickup model REQUEST_MODEL(objModel) WHILE NOT HAS_MODEL_LOADED(objModel) REQUEST_MODEL(objModel) WAIT(0) ENDWHILE // Load interior around pickup if required IF bIsInInterior LOAD_INTERIOR_AROUND_COORDS(theScrap.vCoords) ENDIF // Clear area CLEAR_AREA(theScrap.vCoords, 2.5, FALSE) // Set placement flags and create pickup IF bUseRotate SET_BIT(iPlacementFlags, ENUM_TO_INT(PLACEMENT_FLAG_FIXED)) theScrap.pickup = CREATE_PICKUP_ROTATE(PICKUP_CUSTOM_SCRIPT, theScrap.vCoords, theScrap.vRot, iPlacementFlags, -1, rotOrder, TRUE, objModel) ELSE SET_BIT(iPlacementFlags, ENUM_TO_INT(PLACEMENT_FLAG_SNAP_TO_GROUND)) SET_BIT(iPlacementFlags, ENUM_TO_INT(PLACEMENT_FLAG_ORIENT_TO_GROUND)) SET_BIT(iPlacementFlags, ENUM_TO_INT(PLACEMENT_FLAG_UPRIGHT)) SET_BIT(iPlacementFlags, ENUM_TO_INT(PLACEMENT_FLAG_FIXED)) theScrap.pickup = CREATE_PICKUP(pickupType, theScrap.vCoords, iPlacementFlags, -1, TRUE, objModel) ENDIF // Release model SET_MODEL_AS_NO_LONGER_NEEDED(objModel) ENDIF ENDIF ENDIF // Pickup has successfully been created IF DOES_PICKUP_EXIST(theScrap.pickup) // Mark as active theScrap.bActive = TRUE #IF IS_DEBUG_BUILD IF bDebug_PrintToTTYStuff iDebugScrapCreatedNum++ CPRINTLN(DEBUG_AMBIENT, "[Hidden Packages] Scrap created, so far there are: ", iDebugScrapCreatedNum) ENDIF #ENDIF ENDIF ENDIF ENDIF ENDPROC /// PURPOSE: // When the player is close to the pickup location, create the scrap object PROC CREATE_OBJECT_WHEN_IN_RANGE(SCRAP_PICKUP_DATA& theScrap, MODEL_NAMES objModel, BOOL bIsInInterior = FALSE, FLOAT spawnDistance = CREATE_PICKUP_DISTANCE) // If the player is close to the scrap location IF NOT IS_PED_INJURED(PLAYER_PED_ID()) IF IS_ENTITY_IN_RANGE_COORDS(PLAYER_PED_ID(), theScrap.vCoords, spawnDistance) AND NOT DOES_ENTITY_EXIST(theScrap.obj) REQUEST_MODEL(objModel) WHILE NOT HAS_MODEL_LOADED(objModel) REQUEST_MODEL(objModel) WAIT(0) ENDWHILE IF bIsInInterior LOAD_INTERIOR_AROUND_COORDS(theScrap.vCoords) ENDIF CLEAR_AREA(theScrap.vCoords, 2.5, FALSE) theScrap.obj = CREATE_OBJECT(objModel, theScrap.vCoords) theScrap.bActive = TRUE SET_MODEL_AS_NO_LONGER_NEEDED(objModel) ENDIF ENDIF ENDPROC /// PURPOSE: /// Returns the correct pack stat based on the mission /// PARAMS: /// SCRAP_MISSION theScrapMission = the type of scrap mission this is /// RETURNS: /// The pack stat FUNC INT GET_PACK_STAT(SCRAP_MISSION theScrapMission) IF theScrapMission = SCRAP_SUB RETURN PACKSTAT_SONAR_START ELIF theScrapMission = SCRAP_DIVING RETURN PACKSTAT_DIVING_START ELIF theScrapMission = SCRAP_LETTER RETURN PACKSTAT_LETTER_START ELIF theScrapMission = SCRAP_SHIP RETURN PACKSTAT_UFO_START ELSE RETURN PACKSTAT_TRACT_START ENDIF ENDFUNC /// PURPOSE: /// Returns the correct number of maximum pickups based on the mission /// PARAMS: /// SCRAP_MISSION theScrapMission = the type of scrap mission this is /// RETURNS: /// The max number of pickups in the chosen mission FUNC INT GET_NUM_PARTS(SCRAP_MISSION theScrapMission) IF theScrapMission = SCRAP_SUB RETURN NUMBER_OF_SONAR_PICKUPS ELIF theScrapMission = SCRAP_LETTER RETURN NUMBER_OF_LETTER_SCRAPS ELIF theScrapMission = SCRAP_SHIP RETURN NUMBER_OF_SPACESHIP_PARTS ELIF theScrapMission = SCRAP_TRACT RETURN MAX_TRACT_PICKUPS ELIF theScrapMission = SCRAP_DIVING RETURN NUMBER_OF_DIVING_SCRAPS ELSE RETURN NUMBER_OF_FOR_SALE_SIGNS ENDIF ENDFUNC /// PURPOSE: /// Returns the correct number of incremented pickups (for stats) based on the mission /// PARAMS: /// SCRAP_MISSION theScrapMission = the type of scrap mission this is /// RETURNS: /// The number of incremented pickups (for stats) of the chosen mission FUNC STATSENUM GET_HIDDEN_PACKAGES_NUM(SCRAP_MISSION theScrapMission) IF theScrapMission = SCRAP_SUB RETURN NUM_HIDDEN_PACKAGES_3 ELIF theScrapMission = SCRAP_LETTER RETURN NUM_HIDDEN_PACKAGES_0 ELIF theScrapMission = SCRAP_SHIP RETURN NUM_HIDDEN_PACKAGES_1 ELIF theScrapMission = SCRAP_DIVING RETURN NUM_HIDDEN_PACKAGES_4 ELSE RETURN NUM_HIDDEN_PACKAGES_2 ENDIF ENDFUNC /// PURPOSE: /// Find out how many scraps have been destroyed/collected and return the number /// PARAMS: /// INT iNumScraps = how many scraps there are to collect/destroy in this mission /// SCRAP_MISSION theScrapMission = the type of scrap mission this is /// RETURNS: /// The number of scraps destroyed/collected FUNC INT GET_NUM_SCRAPS_SORTED(SCRAP_MISSION theScrapMission) INT iSortedScraps = 0 INT iScrap = 0 INT ipackNum IF theScrapMission = SCRAP_SALE_SIGNS REPEAT GET_NUM_PARTS(theScrapMission) iScrap IF IS_BIT_SET(g_savedGlobals.sAmbient.iForSaleSignDestroyedFlags, RETURN_BIT_FLAG_SLOT(ENUM_TO_INT(GET_NUM_PARTS(theScrapMission)), iScrap)) iSortedScraps++ ENDIF ENDREPEAT ELIF theScrapMission = SCRAP_PEYOTE //Animal controller sets the counter enum directly. STAT_GET_INT(NUM_HIDDEN_PACKAGES_5, iSortedScraps) ELIF theScrapMission = SCRAP_WILDLIFE //phototgraphyWildlife sets the counter enum directly. STAT_GET_INT(NUM_HIDDEN_PACKAGES_7, iSortedScraps) ELIF theScrapMission = SCRAP_MONKEY //Animal controller sets the counter enum directly. STAT_GET_INT(NUM_HIDDEN_PACKAGES_6, iSortedScraps) ELSE REPEAT (GET_NUM_PARTS(theScrapMission)) iScrap ipackNum = (GET_PACK_STAT(theScrapMission)) + iScrap IF GET_PACKED_STAT_BOOL(INT_TO_ENUM(STATS_PACKED, ipackNum)) iSortedScraps++ ENDIF ENDREPEAT ENDIF RETURN iSortedScraps ENDFUNC /// PURPOSE: /// Reset the packed stats for the mentioned mission to false /// PARAMS: /// SCRAP_MISSION theScrapMission = the type of scrap mission this is /// RETURNS: /// N/A PROC RESET_ALL_PACKAGE_FLAGS_TO_ZERO(SCRAP_MISSION scrpMis) INT i = 0 REPEAT (GET_NUM_PARTS(scrpMis)) i SET_PACKED_STAT_BOOL(INT_TO_ENUM(STATS_PACKED, GET_PACK_STAT(scrpMis)), FALSE) ENDREPEAT STAT_DECREMENT(GET_HIDDEN_PACKAGES_NUM(scrpMis), TO_FLOAT(GET_NUM_SCRAPS_SORTED(scrpMis))) ENDPROC /// PURPOSE: /// Returns whether all the scraps have been flagged as collected. /// PARAMS: /// INT iNumOfScraps = the number of scraps to check /// SCRAP_MISSION theScrapMission = the type of scrap mission this is /// RETURNS: /// TRUE if all the scraps have been flagged as collected, else FALSE FUNC BOOL HAVE_ALL_SCRAPS_BEEN_COMPLETED(SCRAP_MISSION scrMis) INT iScrap IF NOT (scrMis = SCRAP_SALE_SIGNS) IF (GET_NUM_SCRAPS_SORTED(scrMis) < GET_NUM_PARTS(scrMis)) RETURN FALSE ENDIF ELSE REPEAT GET_NUM_PARTS(scrMis) iScrap IF NOT IS_BIT_SET(g_savedGlobals.sAmbient.iForSaleSignDestroyedFlags, RETURN_BIT_FLAG_SLOT(GET_NUM_PARTS(scrMis), iScrap)) RETURN FALSE ENDIF ENDREPEAT ENDIF RETURN TRUE ENDFUNC /// PURPOSE: /// Clears any display messages to do with the scraps /// PARAMS: /// BOOL bMessageDisplayed = if a scrap display message is on screen /// STRING scrapSorted = The string for "so-many scraps sorted" message /// STRING allScrapsSorted = The string for "all scraps sorted" message /// INT iNumScraps = how many scraps there are to collect/destroy in this mission /// SCRAP_MISSION theScrapMission = the type of scrap mission this is /// INT theMessageTimer = the time the message has been on screen /// RETURNS: /// N/A PROC CLEAR_DISPLAY_MESSAGE(BOOL & bMessageDisplayed, STRING scrapSorted, STRING allScrapsSorted, SCRAP_MISSION theScrapMission, INT theMessageTimer) IF bMessageDisplayed IF (GET_GAME_TIMER() - theMessageTimer) > 8000 INT NumberOfScrapsSorted = GET_NUM_SCRAPS_SORTED(theScrapMission) IF (NumberOfScrapsSorted = GET_NUM_PARTS(theScrapMission)) IF IS_THIS_PRINT_BEING_DISPLAYED(allScrapsSorted) CLEAR_PRINTS() ENDIF bMessageDisplayed = FALSE ELSE BEGIN_TEXT_COMMAND_IS_MESSAGE_DISPLAYED(scrapSorted) ADD_TEXT_COMPONENT_INTEGER(NumberOfScrapsSorted) IF END_TEXT_COMMAND_IS_MESSAGE_DISPLAYED() CLEAR_PRINTS() ENDIF bMessageDisplayed = FALSE ENDIF ENDIF ENDIF ENDPROC /// PURPOSE: /// Prints the display messages to do with the scraps /// PARAMS: /// STRING scrapSorted = The string for "so-many scraps sorted" message /// STRING allScrapsSorted = The string for "all scraps sorted" message /// INT iNumScraps = how many scraps there are to collect/destroy in this mission /// SCRAP_MISSION theScrapMission = the type of scrap mission this is /// RETURNS: /// TRUE if message printed and help is clear, otherwise returns false FUNC BOOL PRINT_DISPLAY_MESSAGE(STRING allScrapsSorted, STRING scrapSorted, SCRAP_MISSION scrapMissionType) INT iMessageNumber = GET_NUM_SCRAPS_SORTED(scrapMissionType) #IF IS_DEBUG_BUILD IF bDebug_PrintToTTYStuff CPRINTLN(DEBUG_AMBIENT, "[Hidden Packages] NUMBER OF SCRAPS SORTED IS: ", iMessageNumber) ENDIF #ENDIF // Print the new message if help is clear IF NOT IS_MESSAGE_BEING_DISPLAYED() OR NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED() IF HAVE_ALL_SCRAPS_BEEN_COMPLETED(scrapMissionType) PRINT_NOW(allScrapsSorted, DEFAULT_GOD_TEXT_TIME, 1) ELSE PRINT_WITH_NUMBER(scrapSorted, iMessageNumber, DEFAULT_GOD_TEXT_TIME, 1) ENDIF RETURN TRUE ENDIF RETURN FALSE ENDFUNC /// PURPOSE: /// Delete scrap if Player is far away enough not to see it /// PARAMS: /// OBJECT_INDEX scrapObj = the scrap pickup /// BOOL& canPickUpInVeh = if the player's allowed to pick up the scrap in a vehicle /// RETURNS: /// The number of scraps destroyed/collected PROC REMOVE_SCRAP_WHEN_OUT_OF_RANGE(PICKUP_INDEX& scrapPickup, BOOL& bIsScrapActive) // If the player is no longer near the scrap IF NOT IS_PED_INJURED(PLAYER_PED_ID()) IF GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(PLAYER_PED_ID()), GET_PICKUP_COORDS(scrapPickup)) >= CLEANUP_PICKUP_DISTANCE SAFE_REMOVE_PICKUP(scrapPickup) scrapPickup = NULL bIsScrapActive = FALSE #IF IS_DEBUG_BUILD IF bDebug_PrintToTTYStuff iDebugScrapCreatedNum-- CPRINTLN(DEBUG_AMBIENT, "[Hidden Packages] Scrap removed, so far there are: ", iDebugScrapCreatedNum) ENDIF #ENDIF ENDIF ENDIF ENDPROC /// PURPOSE: /// Delete scrap if Player is far away enough not to see it /// PARAMS: /// OBJECT_INDEX scrapObj = the scrap object /// BOOL& canPickUpInVeh = if the player's allowed to pick up the scrap in a vehicle /// RETURNS: /// The number of scraps destroyed/collected PROC REMOVE_OBJECT_WHEN_OUT_OF_RANGE(OBJECT_INDEX& scrapObj, BOOL& bIsScrapActive) // If the player is no longer near the scrap IF NOT IS_PED_INJURED(PLAYER_PED_ID()) IF GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(PLAYER_PED_ID()), GET_ENTITY_COORDS(scrapObj)) >= CLEANUP_OBJECT_DISTANCE AND NOT IS_ENTITY_ON_SCREEN(scrapObj) SAFE_DELETE_OBJECT(scrapObj) bIsScrapActive = FALSE ENDIF ENDIF ENDPROC /// PURPOSE: /// Retuns whether a specified submarine scrap is collected or not /// PARAMS: /// the index of the submarine scrap you want /// RETURNS: /// TRUE if scrap picked up, else FALSE FUNC BOOL HAS_PLAYER_PICKED_UP_SCRAP(SCRAP_MISSION scrapMissionType, INT index) INT ipackNum INT ipackStart IF (scrapMissionType = SCRAP_LETTER) ipackStart = PACKSTAT_LETTER_START ELIF (scrapMissionType = SCRAP_SHIP) ipackStart = PACKSTAT_UFO_START ELIF (scrapMissionType = SCRAP_SUB) ipackStart = PACKSTAT_SONAR_START ELIF (scrapMissionType = SCRAP_TRACT) ipackStart = PACKSTAT_TRACT_START ELIF (scrapMissionType = SCRAP_DIVING) ipackStart = PACKSTAT_DIVING_START ENDIF ipackNum = ipackStart + index RETURN GET_PACKED_STAT_BOOL(INT_TO_ENUM(STATS_PACKED, ipackNum)) ENDFUNC #IF IS_DEBUG_BUILD USING "shared_debug.sch" /// PURPOSE: /// Warps ped near scrap or for sale sign if need be /// PARAMS: /// VECTOR vTempScrapCoord = scrap's coords /// FLOAT fTempScrapHeading = scraps heading /// INT iScrap = number of the scrap /// RETURNS: /// N/A PROC OUTPUT_TEMP_DATA(VECTOR vTempScrapCoord, FLOAT fTempScrapHeading, INT iScrap) TEXT_LABEL_63 tlTemp OPEN_DEBUG_FILE() SAVE_NEWLINE_TO_DEBUG_FILE() tlTemp = "vScrapCoords[" tlTemp += iScrap tlTemp += "] = " SAVE_STRING_TO_DEBUG_FILE(tlTemp) SAVE_VECTOR_TO_DEBUG_FILE(vTempScrapCoord) SAVE_STRING_TO_DEBUG_FILE(" ") tlTemp = "fScrapHeading[" tlTemp += iScrap tlTemp += "] = " SAVE_STRING_TO_DEBUG_FILE(tlTemp) SAVE_FLOAT_TO_DEBUG_FILE(fTempScrapHeading) SAVE_NEWLINE_TO_DEBUG_FILE() CLOSE_DEBUG_FILE() ENDPROC /// PURPOSE: /// Warps player near scrap or for sale sign if need be /// PARAMS: /// BOOL& bWarp = If warping is needed /// VECTOR vScrapCoords = scrap's coords PROC CHECK_WARP_TO_PACKAGE(BOOL& bWarp, VECTOR vScrapCoords) IF bWarp LOAD_SCENE(vScrapCoords) SAFE_TELEPORT_ENTITY(PLAYER_PED_ID(), vScrapCoords+<<0.0, -1.5, 0.0>>, 0.0) IF IS_PED_SWIMMING_UNDER_WATER(PLAYER_PED_ID()) //so player doesn't automatically collect it SAFE_TELEPORT_ENTITY(PLAYER_PED_ID(), vScrapCoords+<<0.0, -2.5, 0.0>>, 0.0) FORCE_PED_MOTION_STATE(PLAYER_PED_ID(), MS_DIVING_IDLE) ENDIF bWarp = FALSE ENDIF ENDPROC /// PURPOSE: /// Prints the number in the parameter /// PARAMS: /// INT iScrapNum = the scrap number PROC PRINT_SCRAP_NUMBER(INT iScrapNum) CPRINTLN(DEBUG_AMBIENT,"[Hidden Packages] Collected scrap: ", iScrapNum) iDebugScrapCreatedNum-- CPRINTLN(DEBUG_AMBIENT,"[Hidden Packages] So far there are: ", iDebugScrapCreatedNum) ENDPROC /// PURPOSE: /// Blips scrap or for sale sign if need be /// PARAMS: /// BOOL bMapScraps = if the scraps need mapping (TRUE if they do else FALSE) /// BLIP_INDEX scrapBlip = the scrap blip /// INT iScrap = the scrap number /// INT iNumScrapParts = number of scrap parts /// SCRAP_MISSION scrapMissionType = the scrap mission type /// VECTOR vScrapCoord = coord of the scrap /// RETURNS: /// N/A PROC MAP_SCRAP_CHECK(BOOL bMapScraps, BLIP_INDEX& scrapBlip, INT iScrap, INT iNumScrapParts, SCRAP_MISSION scrapMissionType, VECTOR vScrapCoord) BOOL bCollected = TRUE IF (scrapMissionType = SCRAP_SALE_SIGNS) bCollected = IS_BIT_SET(g_savedGlobals.sAmbient.iForSaleSignDestroyedFlags, RETURN_BIT_FLAG_SLOT(iNumScrapParts, iScrap)) ELSE bCollected = HAS_PLAYER_PICKED_UP_SCRAP( scrapMissionType, iScrap) ENDIF IF bMapScraps IF NOT DOES_BLIP_EXIST(scrapBlip) AND NOT bCollected scrapBlip = ADD_BLIP_FOR_COORD(vScrapCoord) SET_BLIP_SCALE(scrapBlip, 1) SET_BLIP_COLOUR(scrapBlip, BLIP_COLOUR_GREEN) ELSE IF bCollected SAFE_REMOVE_BLIP(scrapBlip) ENDIF ENDIF ELSE SAFE_REMOVE_BLIP(scrapBlip) ENDIF ENDPROC #ENDIF