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

494 lines
16 KiB
Python
Executable File

//////////////////////////////////////////////////////////////////////////////////////////
// //
// SCRIPT NAME : forSaleSigns.sc //
// AUTHOR : Kenneth Ross //
// DESCRIPTION : Sets up 15 For Sale signs around the map for the player to //
// smash up. //
// //
//////////////////////////////////////////////////////////////////////////////////////////
//Compile out Title Update changes to header functions.
//Must be before includes.
//CONST_INT USE_TU_CHANGES 0 // Removed by Kenneth R.
// Includes
USING "rage_builtins.sch"
USING "globals.sch"
USING "commands_script.sch"
USING "commands_entity.sch"
USING "commands_streaming.sch"
USING "commands_object.sch"
USING "dialogue_public.sch"
USING "script_player.sch"
USING "flow_public_core.sch"
USING "CompletionPercentage_public.sch"
USING "savegame_public.sch"
USING "common_packages.sch"
USING "RC_helper_functions.sch"
// Variables
MISSION_STAGE eStage = STAGE_INITIALISE
MODEL_NAMES eSignModel = PROP_FORSALE_LENNY_01
BOOL bDisplayMessage = FALSE
INT iMessageStage = 0
SCALEFORM_INDEX siMessage
BOOL bDoPostJosh1 = FALSE
INT iConvTimer
PED_INDEX pedJosh
structPedsForConversation ConvStruct
SCRAP_PICKUP_DATA sSign[NUMBER_OF_FOR_SALE_SIGNS]
BOOL bMessageOnDisplay
// Debug
#IF IS_DEBUG_BUILD
USING "shared_debug.sch"
// Widgets
WIDGET_GROUP_ID widgetGroup
BOOL bOutputAllTempCoords
BOOL bOutputTempCoords[NUMBER_OF_FOR_SALE_SIGNS]
BOOL bUpdatePositions
BOOL bResetTempPositions
BOOL bWarpToSign[NUMBER_OF_FOR_SALE_SIGNS]
BOOL bUseTempPos[NUMBER_OF_FOR_SALE_SIGNS]
FLOAT fTempScrapHeading[NUMBER_OF_FOR_SALE_SIGNS]
VECTOR vTempScrapCoords[NUMBER_OF_FOR_SALE_SIGNS]
BOOL bMapAllsSign
BOOL bDestroyAllSigns
/// PURPOSE:
/// Creates widgets to teleport Player to the locations, blip locations etc
PROC SETUP_FOR_SALE_WIDGETS()
INT iScrap
TEXT_LABEL_63 tlTemp
widgetGroup = START_WIDGET_GROUP("For Sale Signs")
ADD_WIDGET_BOOL("Show all signs on map", bMapAllsSign)
ADD_WIDGET_BOOL("Destroy all signs", bDestroyAllSigns)
START_WIDGET_GROUP("Positions")
ADD_WIDGET_BOOL("bUpdatePositions", bUpdatePositions)
ADD_WIDGET_BOOL("bResetTempPositions", bResetTempPositions)
ADD_WIDGET_BOOL("bOutputAllTempCoords", bOutputAllTempCoords)
REPEAT NUMBER_OF_FOR_SALE_SIGNS iScrap
tlTemp = "Sign["
tlTemp += iScrap
tlTemp += "]"
START_WIDGET_GROUP(tlTemp)
ADD_WIDGET_BOOL("bWarp", bWarpToSign[iScrap])
ADD_WIDGET_BOOL("bUseTempPos", bUseTempPos[iScrap])
ADD_WIDGET_FLOAT_SLIDER("fHeading", fTempScrapHeading[iScrap], 0.0, 360.0, 1.0)
ADD_WIDGET_VECTOR_SLIDER("vCoords", vTempScrapCoords[iScrap], -4000.0, 4000.0, 0.1)
ADD_WIDGET_BOOL("bOutputTempCoords", bOutputTempCoords[iScrap])
STOP_WIDGET_GROUP()
ENDREPEAT
STOP_WIDGET_GROUP()
START_WIDGET_GROUP("Flags")
ADD_BIT_FIELD_WIDGET("iForSaleSignDestroyedFlags", g_savedGlobals.sAmbient.iForSaleSignDestroyedFlags)
STOP_WIDGET_GROUP()
STOP_WIDGET_GROUP()
ENDPROC
/// PURPOSE:
/// Sorts out the correct functionality depending on if any widgets are selected
PROC MAINTAIN_FOR_SALE_WIDGETS()
INT iScrap
REPEAT NUMBER_OF_FOR_SALE_SIGNS iScrap
// Some signs need different wrap coords to make sure the Playeris warped safely
IF (iScrap = 5)
CHECK_WARP_TO_PACKAGE(bWarpToSign[iScrap], <<-381.1934, 430.2234, 111.2149>>)
ELIF (iScrap = 6)
CHECK_WARP_TO_PACKAGE(bWarpToSign[iScrap], <<-161.3442, 990.9147, 233.8630>>)
ELIF (iScrap = 14)
CHECK_WARP_TO_PACKAGE(bWarpToSign[iScrap], <<-1164.6997, 321.7902, 68.1655>>)
ELSE
CHECK_WARP_TO_PACKAGE(bWarpToSign[iScrap], sSign[iScrap].vCoords)
ENDIF
MAP_SCRAP_CHECK(bMapAllsSign, sSign[iScrap].blip, iScrap, NUMBER_OF_FOR_SALE_SIGNS, SCRAP_SALE_SIGNS, sSign[iScrap].vCoords)
IF bDestroyAllSigns
SET_BIT(g_savedGlobals.sAmbient.iForSaleSignDestroyedFlags, iScrap)
bDisplayMessage = TRUE
ENDIF
IF bUpdatePositions
IF DOES_ENTITY_EXIST(sSign[iScrap].obj)
IF bUseTempPos[iScrap]
SAFE_TELEPORT_ENTITY(sSign[iScrap].obj, vTempScrapCoords[iScrap], fTempScrapHeading[iScrap])
SET_ENTITY_HEADING(sSign[iScrap].obj, fTempScrapHeading[iScrap])
ELSE
SAFE_TELEPORT_ENTITY(sSign[iScrap].obj, sSign[iScrap].vCoords, sSign[iScrap].fHeading)
ENDIF
ENDIF
ENDIF
IF bOutputTempCoords[iScrap]
OR bOutputAllTempCoords
OUTPUT_TEMP_DATA(vTempScrapCoords[iScrap], fTempScrapHeading[iScrap], iScrap)
bOutputTempCoords[iScrap] = FALSE
ENDIF
IF bResetTempPositions
vTempScrapCoords[iScrap] = sSign[iScrap].vCoords
fTempScrapHeading[iScrap] = sSign[iScrap].fHeading
ENDIF
ENDREPEAT
bDestroyAllSigns = FALSE
bResetTempPositions = FALSE
bOutputAllTempCoords = FALSE
ENDPROC
#ENDIF
/// PURPOSE:
/// Tries to grab Josh from the 32 closest peds around the player when called
/// RETURNS:
/// True if found Josh
FUNC BOOL GrabJosh()
INT cnt
INT i
PED_INDEX tmpArray[32]
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
cnt = GET_PED_NEARBY_PEDS(PLAYER_PED_ID(), tmpArray)
ENDIF
REPEAT cnt i
IF IS_ENTITY_ALIVE(tmpArray[i])
IF (GET_ENTITY_MODEL(tmpArray[i]) = GET_NPC_PED_MODEL(CHAR_JOSH))
CPRINTLN(DEBUG_AMBIENT, "For Sale Signs: - Josh get!")
pedJosh = tmpArray[i]
IF NOT IS_ENTITY_A_MISSION_ENTITY(pedJosh)
SET_ENTITY_AS_MISSION_ENTITY(pedJosh)
ENDIF
SET_PED_MONEY(pedJosh, 0)
SET_PED_CAN_BE_TARGETTED(pedJosh, FALSE)
SET_PED_NAME_DEBUG(pedJosh, "POSTJOSH")
SET_PED_RELATIONSHIP_GROUP_HASH(pedJosh, RELGROUPHASH_PLAYER)
ADD_PED_FOR_DIALOGUE(ConvStruct, 5, pedJosh, "JOSH", TRUE)
RETURN TRUE
ENDIF
ENDIF
ENDREPEAT
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Sets up the "For Sale" sign locations
PROC DO_INITIALISE()
CPRINTLN(DEBUG_AMBIENT, "For Sale Signs: DO_INITIALISE()")
// If we've grabbed Josh when starting this script, it must be being launched after completing Josh 1, so let's use this to control him
bDoPostJosh1 = GrabJosh()
IF NOT HAVE_ALL_SCRAPS_BEEN_COMPLETED(SCRAP_SALE_SIGNS)
sSign[0].vCoords = <<-1569.5250, 444.0400, 108.1350>> sSign[0].fHeading = 80.0000
sSign[1].vCoords = <<-1352.8800, 613.7700, 134.3400>> sSign[1].fHeading = 198.0000 // Updated 07/10/11
sSign[2].vCoords = <<-1467.0, -5.5, 53.1050>> sSign[2].fHeading = 104.0000
sSign[3].vCoords = <<-1632.8329, -413.8573, 39.9441>> sSign[3].fHeading = 321.5000 // Updated 18/11/11
sSign[4].vCoords = <<-1765.6779, -423.9763, 41.5>> sSign[4].fHeading = 256.2500
sSign[5].vCoords = <<-380.7500, 427.7900, 110.9700>> sSign[5].fHeading = 286.5000 // Updated 07/10/11
sSign[6].vCoords = <<-163.2826, 989.2324, 234.0641>> sSign[6].fHeading = 0.00 // Updated 18/11/11
sSign[7].vCoords = <<-493.0830, 588.6333, 122.48499>> sSign[7].fHeading = 177.2061 // Updated 02/08/12
sSign[8].vCoords = <<-818.8400, 434.7400, 88.1500>> sSign[8].fHeading = 91.5000
sSign[9].vCoords = << -913.24, 785.40, 184.66 >> sSign[9].fHeading = 96.26
sSign[10].vCoords = <<-825.9200, -26.6700, 38.6800>> sSign[10].fHeading = 15.0000
sSign[11].vCoords = <<-1103.5000, 587.2900, 103.25>> sSign[11].fHeading = 305.5000
sSign[12].vCoords = << -1253.4395, 491.05, 94.3706 >> sSign[12].fHeading = 0.0000
sSign[13].vCoords = << 160.29, 486.71, 142.96 >> sSign[13].fHeading = 95.0000
sSign[14].vCoords = <<-1163.8590, 319.1900, 67.9600>> sSign[14].fHeading = 40.0000 // Updated 07/10/11
eStage = STAGE_PROCESS
ELSE
eStage = STAGE_CLEANUP
ENDIF
ENDPROC
/// PURPOSE:
/// Print a message stating how many signs have been destroyed if this is needed...
/// Now using the Midsized message UI as per Les request
PROC UPDATE_SALE_SIGN_MESSAGE()
INT iSplashSoundID
IF bDisplayMessage
SWITCH iMessageStage
CASE 0
IF NOT IS_SCREEN_FADED_OUT()
siMessage = REQUEST_SCALEFORM_MOVIE("MIDSIZED_MESSAGE")
IF HAS_SCALEFORM_MOVIE_LOADED(siMessage)
iSplashSoundID = GET_SOUND_ID()
PLAY_SOUND_FRONTEND(iSplashSoundID, "SIGN_DESTROYED", "HUD_AWARDS")
iMessageStage++
ENDIF
ENDIF
BREAK
CASE 1
BEGIN_SCALEFORM_MOVIE_METHOD(siMessage, "SHOW_SHARD_MIDSIZED_MESSAGE")
BEGIN_TEXT_COMMAND_SCALEFORM_STRING("FS_TITLE")
END_TEXT_COMMAND_SCALEFORM_STRING()
BEGIN_TEXT_COMMAND_SCALEFORM_STRING("FS_DESTROY")
ADD_TEXT_COMPONENT_INTEGER(GET_NUM_SCRAPS_SORTED(SCRAP_SALE_SIGNS))
END_TEXT_COMMAND_SCALEFORM_STRING()
END_SCALEFORM_MOVIE_METHOD()
SETTIMERB(0)
iMessageStage++
BREAK
CASE 2
IF TIMERB() > DEFAULT_GOD_TEXT_TIME - 500
BEGIN_SCALEFORM_MOVIE_METHOD(siMessage, "SHARD_ANIM_OUT")
SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(ENUM_TO_INT(HUD_COLOUR_WHITE))
SCALEFORM_MOVIE_METHOD_ADD_PARAM_FLOAT(0.33)
END_SCALEFORM_MOVIE_METHOD()
iMessageStage++
ELSE
IF NOT IS_RESULT_SCREEN_DISPLAYING()
IF HAS_SCALEFORM_MOVIE_LOADED(siMessage)
DRAW_SCALEFORM_MOVIE_FULLSCREEN(siMessage, 100, 100, 100, 255)
ENDIF
ENDIF
ENDIF
BREAK
CASE 3
IF TIMERB() > DEFAULT_GOD_TEXT_TIME
iMessageStage++
ELSE
IF NOT IS_RESULT_SCREEN_DISPLAYING()
IF HAS_SCALEFORM_MOVIE_LOADED(siMessage)
DRAW_SCALEFORM_MOVIE_FULLSCREEN(siMessage, 100, 100, 100, 255)
ENDIF
ENDIF
ENDIF
BREAK
CASE 4
IF HAS_SCALEFORM_MOVIE_LOADED(siMessage)
SET_SCALEFORM_MOVIE_AS_NO_LONGER_NEEDED(siMessage)
ENDIF
bMessageOnDisplay = FALSE
bDisplayMessage = FALSE
iMessageStage = 0
BREAK
ENDSWITCH
ENDIF
ENDPROC
/// PURPOSE:
/// Checks to see if the sign has been 'knocked over' by checking it X and Y rotation to see if it's not standing up straight anymore
/// PARAMS:
/// sign - The sign we're checking
/// RETURNS:
/// True if the X or Y are beyond certain bounds, False otherwise
FUNC BOOL HAS_SIGN_TIPPED_OVER(ENTITY_INDEX sign)
VECTOR testRot = GET_ENTITY_ROTATION(sign, EULER_XYZ)
// If the sign has an X or Y rot more than 30.0 or less than -30.0, consider it knocked over
IF (testRot.X > 30.0)
OR (testRot.X < -30.0)
OR (testRot.Y > 30.0)
OR (testRot.Y < -30.0)
RETURN TRUE // The sign has tipped over
ENDIF
RETURN FALSE
ENDFUNC
PROC SET_SIGN_STAT(int iSign)
IF iSign < 16
SET_PACKED_STAT_BOOL( INT_TO_ENUM(STATS_PACKED, ENUM_TO_INT(PACKED_NUM_DESTROYED_SIGNS_START) + iSign) , TRUE)
ELSE
SCRIPT_ASSERT("For sale signs SET_SIGN_STAT(): Destroyed an unknown sign number?")
ENDIF
QUICK_INCREMENT_INT_STAT(NUM_DESTROYED_SIGNS_0, 1)
ENDPROC
/// PURPOSE:
/// Creates/Removes the "For Sale" sign prop when necessary and maintains saved globals
PROC DO_PROCESS()
IF bDoPostJosh1
IF IS_ENTITY_ALIVE(pedJosh)
// If the player is <=3m from Josh, play one of his ambient lines, with an 8sec delay between them
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
AND NOT IS_PED_FLEEING(pedJosh)
AND NOT IS_PED_RAGDOLL(pedJosh)
AND NOT IS_PED_PRONE(pedJosh)
AND NOT IS_PED_INJURED(pedJosh)
IF GET_DISTANCE_BETWEEN_ENTITIES(pedJosh, PLAYER_PED_ID()) <= 3.0
IF (GET_GAME_TIMER() - iConvTimer) > 8000
IF CREATE_CONVERSATION(ConvStruct, "JOSH1AU", "JOSH1_AMB2", CONV_PRIORITY_MEDIUM)
iConvTimer = GET_GAME_TIMER()
ENDIF
ENDIF
ENDIF
ENDIF
IF GET_DISTANCE_BETWEEN_ENTITIES(pedJosh, PLAYER_PED_ID()) > 150.0
SAFE_DELETE_PED(pedJosh) // Josh won't be alive after this, it'll set the bool to false the frame after
ENDIF
ELSE
SAFE_RELEASE_PED(pedJosh) // In case he still exists but is dead - no point holding onto the ped index anymore
bDoPostJosh1 = FALSE
ENDIF
ENDIF
INT iScrap = 0
REPEAT NUMBER_OF_FOR_SALE_SIGNS iScrap
IF DOES_ENTITY_EXIST(sSign[iScrap].obj)
// If the sign has been destroyed...
IF HAS_OBJECT_BEEN_BROKEN(sSign[iScrap].obj)
OR HAS_SIGN_TIPPED_OVER(sSign[iScrap].obj)
// Flag the sign as destroyed
SET_BIT(g_savedGlobals.sAmbient.iForSaleSignDestroyedFlags, iScrap)
SET_SIGN_STAT(iScrap)
SET_OBJECT_AS_NO_LONGER_NEEDED(sSign[iScrap].obj)
bDisplayMessage = TRUE
SET_AUTOSAVE_IGNORES_ON_MISSION_FLAG(TRUE)
MAKE_AUTOSAVE_REQUEST()
#IF IS_DEBUG_BUILD
PRINT_SCRAP_NUMBER(iScrap)
#ENDIF
// The sign hasn't been destroyed
ELSE
REMOVE_OBJECT_WHEN_OUT_OF_RANGE(sSign[iScrap].obj, sSign[iScrap].bActive)
ENDIF
// The sign has not been created
ELSE
// If the sign has not been destroyed
IF NOT IS_BIT_SET(g_savedGlobals.sAmbient.iForSaleSignDestroyedFlags, iScrap)
CREATE_OBJECT_WHEN_IN_RANGE(sSign[iScrap], eSignModel, FALSE, 200)
ENDIF
ENDIF
ENDREPEAT
UPDATE_SALE_SIGN_MESSAGE()
IF NOT bMessageOnDisplay
AND NOT bDisplayMessage
IF HAVE_ALL_SCRAPS_BEEN_COMPLETED(SCRAP_SALE_SIGNS)
eStage = STAGE_CLEANUP
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Cleans up any assets that were created
PROC CLEANUP_SCRIPT()
CPRINTLN(DEBUG_AMBIENT, "STAGE_CLEANUP")
INT iScrap
REPEAT NUMBER_OF_FOR_SALE_SIGNS iScrap
SAFE_REMOVE_BLIP(sSign[iScrap].blip)
ENDREPEAT
REPEAT NUMBER_OF_FOR_SALE_SIGNS iScrap
SAFE_RELEASE_OBJECT(sSign[iScrap].obj)
ENDREPEAT
SET_MODEL_AS_NO_LONGER_NEEDED(eSignModel)
// Register completion stats when completing normally or via debug
IF HAVE_ALL_SCRAPS_BEEN_COMPLETED(SCRAP_SALE_SIGNS)
OR GET_MISSION_FLOW_FLAG_STATE(FLOWFLAG_FOR_SALE_SIGNS_DESTROYED) = TRUE
// Set Josh 1 completion percentage
REGISTER_SCRIPT_IN_COMPLETION_PERCENTAGE_TOTAL ( CP_RAND_C_JOS1 )
// No longer require the script to be relaunched when loading from a savegame.
Remove_Script_From_Relaunch_List(LAUNCH_BIT_RC_AMB_FOR_SALE_SIGNS)
UNLOCK_TEXT_MESSAGE_BY_LABEL("JOSH1_TXT")
DELETE_TEXT_MESSAGE_BY_LABEL_FROM_CURRENT_PLAYER("JOSH1_TXT")
IF GET_MISSION_FLOW_FLAG_STATE(FLOWFLAG_FOR_SALE_SIGNS_DESTROYED) = FALSE
// Send a delayed text from Josh that unlocks RC Josh 2
// only send text if flow flag isn't set
// if the flow flag is set, we've debug skipped past Josh 2
// Don't use CT_END_OF_MISSION for this communication as that blocks
// switching and a number of other things until it is sent. -BenR
REGISTER_TEXT_MESSAGE_FROM_CHARACTER_TO_PLAYER( TEXT_FORSALESIGNS_DONE,
CT_FLOW, BIT_TREVOR, CHAR_JOSH,
30000, 10000,
VID_BLANK, CID_JOSH1_SIGNS_DESTROYED)
ENDIF
ENDIF
#IF IS_DEBUG_BUILD
IF DOES_WIDGET_GROUP_EXIST(widgetGroup)
DELETE_WIDGET_GROUP(widgetGroup)
ENDIF
#ENDIF
TERMINATE_THIS_THREAD()
ENDPROC
SCRIPT
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)
CLEANUP_SCRIPT()
ENDIF
IF GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH (HASH("forSaleSigns")) > 1
CPRINTLN(DEBUG_AMBIENT, "For Sale Signs: 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_RC_AMB_FOR_SALE_SIGNS)
#IF IS_DEBUG_BUILD
SETUP_FOR_SALE_WIDGETS()
#ENDIF
// Main loop
WHILE TRUE
WAIT(0)
// Immediately cleanup if this script has been completed via debug
IF GET_MISSION_FLOW_FLAG_STATE(FLOWFLAG_FOR_SALE_SIGNS_DESTROYED) = TRUE
CLEANUP_SCRIPT()
ENDIF
SWITCH eStage
CASE STAGE_INITIALISE
DO_INITIALISE()
BREAK
CASE STAGE_PROCESS
DO_PROCESS()
BREAK
CASE STAGE_CLEANUP
CLEANUP_SCRIPT()
BREAK
ENDSWITCH
#IF IS_DEBUG_BUILD
MAINTAIN_FOR_SALE_WIDGETS()
#ENDIF
ENDWHILE
ENDSCRIPT