Files
gtav-src/script/dev_ng/singleplayer/scripts/Minigames/Tennis/Tennis.sch
T
2025-09-29 00:52:08 +02:00

1203 lines
44 KiB
XML
Executable File

USING "minigames_helpers.sch"
USING "minigame_UIInputs.sch"
USING "script_oddjob_funcs.sch"
USING "net_celebration_screen.sch"
USING "tennis_ai.sch"
USING "tennis_court.sch"
USING "tennis_player.sch"
USING "tennis_ball.sch"
USING "tennis_ui.sch"
USING "tennis_cameras.sch"
USING "tennis_debug.sch"
USING "MP_Scaleform_Functions.sch"
CONST_INT MAX_SETS 5
CONST_FLOAT fBaseRewardMoney 25.0
CONST_FLOAT DEFAULT_FORCE_SCALE 0.1
CONST_FLOAT DEFAULT_DAMPENING 4.0
CONST_FLOAT DEFAULT_MASS 1.0
CONST_FLOAT fBallOffsetX 0.110
CONST_FLOAT fBallOffsetY 0.000
CONST_FLOAT fBallOffsetZ 0.040
CONST_FLOAT fDragApproximation 0.82
CONST_FLOAT RacketOffX 0.1
CONST_FLOAT RacketOffY 0.04
CONST_FLOAT RacketOffZ -0.01
CONST_FLOAT RacketRotX -80.96
CONST_FLOAT RacketRotY 3.59
CONST_FLOAT RacketRotZ -21.54
CONST_FLOAT POWER_STAT_MULT 0.1
CONST_FLOAT SPEED_MOD 0.1
CONST_FLOAT ANIM_PLAYING_TIME 3.550
CONST_FLOAT CAM_OFFSET_HEADING 80.0
CONST_FLOAT CAM_LOOK_AT_MULT 6.535
CONST_FLOAT CAM_OFFSET_X 4.113
CONST_FLOAT CAM_OFFSET_Y -6.157
CONST_FLOAT CAM_OFFSET_Z 6.3
CONST_INT SHAKE_DURATION 1
CONST_INT SHAKE_FREQ 65
CONST_FLOAT RALLY_CONVO_RATE 0.25
CONST_FLOAT CUT_DELAY 0.4
CONST_FLOAT CUT_DELAY_ONLINE 0.5
CONST_FLOAT QUIT_PT_RADIUS 2.925
CONST_FLOAT QUIT_PT_RESET 1.25
CONST_FLOAT SKIP_INTER_DELAY 0.25
CONST_FLOAT DISABLED_INPUT_DEAD_ZONE 0.2
CONST_FLOAT HURRY_SPEECH_DELAY 15.0
// Rob, if you have a better place for this please move it
STRUCTTIMER m_timerSkip
/// ******************************************************* ENUMS ***************************************************************************
ENUM TENNIS_GAME_STATE_ENUM
TGSE_PRE_INIT = 0,
TGSE_PRE_INIT_WAIT,
TGSE_FRIEND_INIT_WAIT,
TGSE_CAMERA_PAN_WAIT,
TGSE_INIT,
TGSE_INTRO, //5
TGSE_OPTION_MENU,
TGSE_OPTION_FINISHED,
TGSE_PREPARE_SERVE,
TGSE_SERVE,
TGSE_SERVE_RESULT, //10
TGSE_WAIT_FOR_PREDICTION,
TGSE_RALLY,
TGSE_RALLY_PREDICT,
TGSE_POINT_WON,
TGSE_POINT_CHECKS, //Used in MP to wait until scores are synced //15
TGSE_INTERSTITIAL,
TGSE_SIDE_CHANGE,
TGSE_CHANGE_SERVERS,
TGSE_SCORE,
TGSE_TENNIS_PLAYERS_WALKING_WAIT, //20
TGSE_QUIT_CONFIRM,
TGSE_OUTRO,
TGSE_OUTRO_FROM_OPTIONS,
TGSE_QUIT_FROM_OPTION_MENU,
TGSE_FINISH, //25
TGSE_CLEANUP,
TGSE_PREPARE_REMATCH,
TGSE_TEST_STATE
ENDENUM
FUNC STRING GET_STRING_FROM_TENNIS_GAME_STATE(TENNIS_GAME_STATE_ENUM eState)
SWITCH eState
CASE TGSE_PRE_INIT RETURN "TGSE_PRE_INIT"
CASE TGSE_PRE_INIT_WAIT RETURN "TGSE_PRE_INIT_WAIT"
CASE TGSE_FRIEND_INIT_WAIT RETURN "TGSE_FRIEND_INIT_WAIT"
CASE TGSE_CAMERA_PAN_WAIT RETURN "TGSE_CAMERA_PAN_WAIT"
CASE TGSE_INIT RETURN "TGSE_INIT"
CASE TGSE_INTRO RETURN "TGSE_INTRO"
CASE TGSE_OPTION_MENU RETURN "TGSE_OPTION_MENU"
CASE TGSE_OPTION_FINISHED RETURN "TGSE_OPTION_FINISHED"
CASE TGSE_PREPARE_SERVE RETURN "TGSE_PREPARE_SERVE"
CASE TGSE_SERVE RETURN "TGSE_SERVE"
CASE TGSE_SERVE_RESULT RETURN "TGSE_SERVE_RESULT"
CASE TGSE_WAIT_FOR_PREDICTION RETURN "TGSE_WAIT_FOR_PREDICTION"
CASE TGSE_RALLY RETURN "TGSE_RALLY"
CASE TGSE_RALLY_PREDICT RETURN "TGSE_RALLY_PREDICT"
CASE TGSE_POINT_WON RETURN "TGSE_POINT_WON"
CASE TGSE_POINT_CHECKS RETURN "TGSE_POINT_CHECKS"
CASE TGSE_INTERSTITIAL RETURN "TGSE_INTERSTITIAL"
CASE TGSE_SIDE_CHANGE RETURN "TGSE_SIDE_CHANGE"
CASE TGSE_CHANGE_SERVERS RETURN "TGSE_CHANGE_SERVERS"
CASE TGSE_SCORE RETURN "TGSE_SCORE"
CASE TGSE_TENNIS_PLAYERS_WALKING_WAIT RETURN "TGSE_TENNIS_PLAYERS_WALKING_WAIT"
CASE TGSE_QUIT_CONFIRM RETURN "TGSE_QUIT_CONFIRM"
CASE TGSE_OUTRO RETURN "TGSE_OUTRO"
CASE TGSE_OUTRO_FROM_OPTIONS RETURN "TGSE_OUTRO_FROM_OPTIONS"
CASE TGSE_QUIT_FROM_OPTION_MENU RETURN "TGSE_QUIT_FROM_OPTION_MENU"
CASE TGSE_FINISH RETURN "TGSE_FINISH"
CASE TGSE_CLEANUP RETURN "TGSE_CLEANUP"
CASE TGSE_PREPARE_REMATCH RETURN "TGSE_PREPARE_REMATCH"
CASE TGSE_TEST_STATE RETURN "TGSE_TEST_STATE"
ENDSWITCH
RETURN "Unknown TENNIS_GAME_STATE_ENUM"
ENDFUNC
ENUM TENNIS_MP_STAT_TRACKING_ENUM
TMST_TENNIS_TOTAL_POINTS = 0,
TMST_TENNIS_TOTAL_ACES,
TMST_TENNIS_TOTAL_ACED,
TMST_TENNIS_HIGHEST_DEUCE,
TMST_TENNIS_GAMES_WON,
TMST_TENNIS_GAMES_LOST,
TMST_TENNIS_SETS_WON,
TMST_TENNIS_SETS_LOST,
TMST_TENNIS_MATCHES_WON,
TMST_TENNIS_MATCHES_LOST,
TMST_TENNIS_TOTAL_OOB_HITS,
TMST_TENNIS_TOTAL_FAULTS,
TMST_STAT_TOTAL
ENDENUM
#IF IS_DEBUG_BUILD
FUNC STRING GET_STRING_FROM_TENNIS_MP_STAT_TRACKING_ENUM( TENNIS_MP_STAT_TRACKING_ENUM eStat )
SWITCH eStat
CASE TMST_TENNIS_TOTAL_POINTS RETURN "TMST_TENNIS_TOTAL_POINTS"
CASE TMST_TENNIS_TOTAL_ACES RETURN "TMST_TENNIS_TOTAL_ACES"
CASE TMST_TENNIS_TOTAL_ACED RETURN "TMST_TENNIS_TOTAL_ACED"
CASE TMST_TENNIS_HIGHEST_DEUCE RETURN "TMST_TENNIS_HIGHEST_DEUCE"
CASE TMST_TENNIS_GAMES_WON RETURN "TMST_TENNIS_GAMES_WON"
CASE TMST_TENNIS_GAMES_LOST RETURN "TMST_TENNIS_GAMES_LOST"
CASE TMST_TENNIS_SETS_WON RETURN "TMST_TENNIS_SETS_WON"
CASE TMST_TENNIS_SETS_LOST RETURN "TMST_TENNIS_SETS_LOST"
CASE TMST_TENNIS_MATCHES_WON RETURN "TMST_TENNIS_MATCHES_WON"
CASE TMST_TENNIS_MATCHES_LOST RETURN "TMST_TENNIS_MATCHES_LOST"
CASE TMST_TENNIS_TOTAL_OOB_HITS RETURN "TMST_TENNIS_TOTAL_OOB_HITS"
CASE TMST_TENNIS_TOTAL_FAULTS RETURN "TMST_TENNIS_TOTAL_FAULTS"
CASE TMST_STAT_TOTAL RETURN "TMST_STAT_TOTAL"
ENDSWITCH
RETURN "unknown TENNIS_MP_STAT_TRACKING_ENUM"
ENDFUNC
#ENDIF
INT TENNIS_STAT_TRACKING_ARRAY[TMST_STAT_TOTAL]
ENUM LAUNCH_DETAIL_ENUM
LD_INVALID = -1,
LD_SERVE_S_PLUS = 0,
LD_SERVE_S,
LD_SERVE_A,
LD_SERVE_B,
LD_SERVE_C,
LD_SERVE_D,
LD_SERVE_FAULT,
LD_SERVE_OOB,
LD_MID,
LD_OVERHEAD,
MAX_DETAILS
ENDENUM
FUNC STRING GET_STRING_FROM_TENNIS_SERVE_GRADE(LAUNCH_DETAIL_ENUM eServe)
SWITCH eServe
CASE LD_INVALID RETURN "LD_INVALID"
CASE LD_SERVE_S_PLUS RETURN "LD_SERVE_S_PLUS"
CASE LD_SERVE_S RETURN "LD_SERVE_S"
CASE LD_SERVE_A RETURN "LD_SERVE_A"
CASE LD_SERVE_B RETURN "LD_SERVE_B"
CASE LD_SERVE_C RETURN "LD_SERVE_C"
CASE LD_SERVE_D RETURN "LD_SERVE_D"
CASE LD_SERVE_FAULT RETURN "LD_SERVE_FAULT"
CASE LD_SERVE_OOB RETURN "LD_SERVE_OOB"
CASE LD_MID RETURN "LD_MID"
CASE LD_OVERHEAD RETURN "LD_OVERHEAD"
CASE MAX_DETAILS RETURN "MAX_DETAILS"
ENDSWITCH
RETURN "Unknonwn LAUNCH_DETAIL_ENUM enum"
ENDFUNC
ENUM SERVING_SIDE_ENUM
SERVING_FROM_LEFT = 0,
SERVING_FROM_RIGHT = 1
ENDENUM
ENUM OPTION_BIT_ENUMS
TENNIS_OPTION_NEAR_CAMERA_DEFAULT = BIT0,
TENNIS_OPTION_HOLD_RELEASE_SERVE = BIT1,
TENNIS_OPTION_1_SET = BIT2,
TENNIS_OPTION_3_SET = BIT3,
TENNIS_OPTION_5_SET = BIT4
ENDENUM
ENUM OPTIONS_STATES
OPT_NUM_SETS,
OPT_DIFFICULTY,
OPT_FINISHED,
OPT_SERVE,
OPT_CAMERA
ENDENUM
ENUM TENNIS_ACTIVITY
TA_PLAYER_VS_AI,
TA_AI_VS_AI,
TA_FRIEND_ACTIVITY,
TA_FAMILY_ACTIVITY,
TA_MULTIPLAYER
ENDENUM
//Bitmasks needed to transmit info from PROCESS_GAME_STATE to other update functions involving UI
//When the bitmask is not needed in PROCESS_GAME_STATE then it can be moved to a different bitmask to make for more room in this enum
ENUM TENNIS_UTILS
TD_FIRST_SERVE = BIT0, //Used to blend into the first serve of the match from the options screen
TD_UPDATE_SCORES = BIT1, //
TD_SHOW_TIE_BREAK = BIT2, //set when we want to show the Tie Breaks message
TD_MATCH_POINT_DSTAR = BIT3, //MGTN_matp, tho this is an indirect flag that makes the serve flag flip appropriately
TD_SERVE_DSTAR = BIT4, //MGTN_serv1
TD_FAULT_DSTAR = BIT5, //MGTN_bad
TD_POINT_W_DSTAR = BIT6, //MGTN_winp
TD_ACE = BIT7, //Used when showing an Aced serve
TD_GAME_WON_DSTAR = BIT8, //MGTN_win
TD_GAME_LOST_DSTAR = BIT9, //MGTN_LOS
TD_POINT_L_DSTAR = BIT10, //MGTN_losp
TD_PAUSE_THE_CUT = BIT11, //Used for waiting a beat after a fault, double fault, or OOB
TD_OOB = BIT12,
TD_FAULT = BIT13,
TD_PAUSE_DIALOGUE = BIT14,
TD_GAME_POINT = BIT15,
TD_DEUCE = BIT16,
TD_ADVANTAGE = BIT17,
TD_POINT_WON = BIT18,
TD_POINT_LOST = BIT19,
TD_GAME_WON = BIT20,
TD_GAME_LOST = BIT21,
TD_ANIM_PLAYING = BIT22,
TD_GAME_DONE_STISH = BIT23, //Set when the game is won, check in conjunction with Happy and Sad bits
TD_OPP_HAPPY = BIT24,
TD_OPP_SAD = BIT25,
TD_PLR_HAPPY = BIT26,
TD_PLR_SAD = BIT27,
TD_DELAY_THE_CUT_DELAY = BIT28,
TD_X_MARKER = BIT29,
TD_QUIT_BIT = BIT30
ENDENUM
/// PURPOSE: Keeping tabs on the single player gameflow from state to state. Held in thisProps.
ENUM TENNIS_FLOW_FLAGS
TFF_WALKING_TO_POSITION = BIT0,
TFF_STOP_INTERSTITIAL_CAM_POINTING = BIT1,
TFF_BALL_RESET_IN_HAND = BIT2,
TFF_BASELINE_TRACKING_INIT = BIT3,
TFF_GAME_HAS_SHOWN_SIDE_CHANGE_MSG = BIT4,
TFF_CALL_SIDE_CHANGE_ONCE = BIT5,
TFF_HANDLE_UI_AFTER_SIDE_CHANGE = BIT6,
TFF_HANDLE_NEW_SERVE_MSG = BIT7,
TFF_SHOWN_SERVE_MESSAGE = BIT8,
TFF_CONTINUE_AFTER_VOTE_PASSING = BIT9,
TFF_SCLB_RANK_PRED_DONE = BIT10
ENDENUM
FUNC STRING GET_STRING_FROM_TENNIS_FLOW_FLAGS(TENNIS_FLOW_FLAGS eFlag)
SWITCH eFlag
CASE TFF_WALKING_TO_POSITION RETURN "TFF_WALKING_TO_POSITION"
CASE TFF_STOP_INTERSTITIAL_CAM_POINTING RETURN "TFF_STOP_INTERSTITIAL_CAM_POINTING"
CASE TFF_BALL_RESET_IN_HAND RETURN "TFF_BALL_RESET_IN_HAND"
CASE TFF_BASELINE_TRACKING_INIT RETURN "TFF_BASELINE_TRACKING_INIT"
CASE TFF_GAME_HAS_SHOWN_SIDE_CHANGE_MSG RETURN "TFF_GAME_HAS_SHOWN_SIDE_CHANGE_MSG"
CASE TFF_CALL_SIDE_CHANGE_ONCE RETURN "TFF_CALL_SIDE_CHANGE_ONCE"
CASE TFF_HANDLE_UI_AFTER_SIDE_CHANGE RETURN "TFF_HANDLE_UI_AFTER_SIDE_CHANGE"
CASE TFF_HANDLE_NEW_SERVE_MSG RETURN "TFF_HANDLE_NEW_SERVE_MSG"
CASE TFF_SHOWN_SERVE_MESSAGE RETURN "TFF_SHOWN_SERVE_MESSAGE"
CASE TFF_CONTINUE_AFTER_VOTE_PASSING RETURN "TFF_CONTINUE_AFTER_VOTE_PASSING"
CASE TFF_SCLB_RANK_PRED_DONE RETURN "TFF_SCLB_RANK_PRED_DONE"
ENDSWITCH
RETURN "Unknown TENNIS_FLOW_FLAGS enum"
ENDFUNC
ENUM TENNIS_CONVO_FLAGS
TCVF_MIKE_TREVOR_SERVE_PLAYED = BIT0,
TCVF_LONG_RALLY = BIT1,
TCVF_MIKE_AMANDA_SERVE_PLAYED = BIT2,
TCVF_TREVOR_AMANDA_SERVE_PLAYED = BIT3,
TCVF_AMANDA_SERVE_TO_MICHAEL = BIT4,
TCVF_AMANDA_SERVE_TO_TREVOR = BIT5,
TCVF_AMANDA_CHAT_BAD_SELF = BIT6,
TCVF_MIKE_CHAT = BIT7,
TCVF_INTRO_OPP_HECKLE = BIT8,
TCVF_INTRO_PLYR_RETORT = BIT9,
TCVF_PLAY_AS_AMBIENT_SPEECH = BIT10,
TCVF_AMANDA_GETS_LAST_WORD = BIT11
ENDENUM
FUNC STRING GET_STRING_FROM_TENNIS_CONVO_FLAG(TENNIS_CONVO_FLAGS eFlag)
SWITCH eFlag
CASE TCVF_MIKE_TREVOR_SERVE_PLAYED RETURN "TCVF_MIKE_TREVOR_SERVE_PLAYED"
CASE TCVF_LONG_RALLY RETURN "TCVF_LONG_RALLY"
CASE TCVF_MIKE_AMANDA_SERVE_PLAYED RETURN "TCVF_MIKE_AMANDA_SERVE_PLAYED"
CASE TCVF_TREVOR_AMANDA_SERVE_PLAYED RETURN "TCVF_TREVOR_AMANDA_SERVE_PLAYED"
CASE TCVF_AMANDA_SERVE_TO_MICHAEL RETURN "TCVF_AMANDA_SERVE_TO_MICHAEL"
CASE TCVF_AMANDA_SERVE_TO_TREVOR RETURN "TCVF_AMANDA_SERVE_TO_TREVOR"
CASE TCVF_AMANDA_CHAT_BAD_SELF RETURN "TCVF_AMANDA_CHAT_BAD_SELF"
CASE TCVF_MIKE_CHAT RETURN "TCVF_MIKE_CHAT"
CASE TCVF_PLAY_AS_AMBIENT_SPEECH RETURN "TCVF_PLAY_AS_AMBIENT_SPEECH"
CASE TCVF_INTRO_OPP_HECKLE RETURN "TCVF_INTRO_OPP_HECKLE"
CASE TCVF_INTRO_PLYR_RETORT RETURN "TCVF_INTRO_PLYR_RETORT"
CASE TCVF_AMANDA_GETS_LAST_WORD RETURN "TCVF_AMANDA_GETS_LAST_WORD"
ENDSWITCH
RETURN "unknown TENNIS_CONVO_FLAGS enum"
ENDFUNC
ENUM TENNIS_MATCH_RULESET
TMR_SETS = 0,
TMR_ONE_GAME,
TMR_THREE_GAMES,
TMR_FIVE_GAMES,
TMR_MAX_RULES
ENDENUM
FUNC STRING GET_STRING_FROM_TENNIS_RULESET(TENNIS_MATCH_RULESET eRules)
RETURN PICK_STRING(eRules = TMR_SETS, "TMR_SETS",
PICK_STRING(eRules = TMR_ONE_GAME, "TMR_ONE_GAME",
PICK_STRING(eRules = TMR_THREE_GAMES, "TMR_THREE_GAMES",
PICK_STRING(eRules = TMR_FIVE_GAMES, "TMR_FIVE_GAMES",
PICK_STRING(eRules = TMR_MAX_RULES, "TMR_MAX_RULES",
"Unknown Ruleset")))))
ENDFUNC
ENUM TENNIS_SP_FLAGS
TSP_PICK_OUT_ABUSIVE_PED = BIT0,
TSP_MONITOR_ABUSE_SEQUENCE = BIT1,
TSP_GUARANTEE_FIRST_ABUSE = BIT2,
TSP_PRINT_FIRST_TUT_HELP = BIT3,
TSP_PRINT_SECOND_TUT_HELP = BIT4,
TSP_TUT_SERVE_SET = BIT5
ENDENUM
FUNC STRING GET_STRING_FROM_TENNIS_SP_FLAG(TENNIS_SP_FLAGS eFlag)
SWITCH eFlag
CASE TSP_PICK_OUT_ABUSIVE_PED RETURN "TSP_PICK_OUT_ABUSIVE_PED"
CASE TSP_MONITOR_ABUSE_SEQUENCE RETURN "TSP_MONITOR_ABUSE_SEQUENCE"
CASE TSP_GUARANTEE_FIRST_ABUSE RETURN "TSP_GUARANTEE_FIRST_ABUSE"
CASE TSP_PRINT_FIRST_TUT_HELP RETURN "TSP_PRINT_FIRST_TUT_HELP"
CASE TSP_PRINT_SECOND_TUT_HELP RETURN "TSP_PRINT_SECOND_TUT_HELP"
CASE TSP_TUT_SERVE_SET RETURN "TSP_TUT_SERVE_SET"
ENDSWITCH
RETURN "Unknown TennisSP Flag"
ENDFUNC
ENUM TENNIS_SERVE_VALIDATION
TENNIS_SERVE_INVALID,
TENNIS_SERVE_SUCCESS,
TENNIS_SERVE_FAILSAFE
ENDENUM
FUNC STRING GET_STRING_FROM_TENNIS_SERVE_VALIDATION(TENNIS_SERVE_VALIDATION eValidation)
SWITCH eValidation
CASE TENNIS_SERVE_INVALID RETURN "TENNIS_SERVE_INVALID"
CASE TENNIS_SERVE_SUCCESS RETURN "TENNIS_SERVE_SUCCESS"
CASE TENNIS_SERVE_FAILSAFE RETURN "TENNIS_SERVE_FAILSAFE"
ENDSWITCH
RETURN "unknown TENNIS_SERVE_VALIDATION"
ENDFUNC
/// ******************************************************* STRUCTS ***************************************************************************
STRUCT TENNIS_SWING_PHASES
FLOAT fInit = -1.0
FLOAT fActive = -1.0
FLOAT fEnd = -1.0
INT iFrameTot = -1
TEXT_LABEL_31 sAnimName
ENDSTRUCT
STRUCT TENNIS_SCORES
INT iPointsWon
INT iGamesWon[MAX_SETS]
INT iSetsWon
ENDSTRUCT
STRUCT TENNIS_GAME_PROPERTIES
//Single AND Multi
TENNIS_PLAYER tennisPlayers[TENNIS_PLAYERS]
TENNIS_COURT tennisCourt
TENNIS_BALL tennisBall
INT iFrameCounter
LAUNCH_DETAILS grades[MAX_DETAILS]
VECTOR vSwingStateOffsets[TSS_MAX_SWINGS]
TENNIS_SWING_PHASES phasesArray[TSS_MAX_SWINGS]
TENNIS_SWING_STATES swingBuckets[MAX_SWING_BUCKETS][MAX_SWINGS_PER_BUCKET]
FLOAT swingBucketSteps[MAX_SWING_BUCKETS+1]
VECTOR vFuturePoints[MAX_FUTURE_POINTS]
enumActivityLocation thisLocation
TENNIS_ACTIVITY eTennisActivity
VECTOR vForward,vRight,vUp // Rereference frame
BOOL bCheckPredictionFlag
MODEL_NAMES mTheOpponentModel
MODEL_NAMES mThePlayerAIModel
TEXT_LABEL_7 texIdleEvent
INT iTimerA
TENNIS_FLOW_FLAGS eFlowFlags
INT iBallResetStamp
structTimer tServe
INT iDelayNextUITimeStamp // used to delay the tiebreak message
TENNIS_PLAYER_ID eCachedScoredLast // Changed locally because we want to start the walk sooner but can't rely on server data
#IF IS_TENNIS_MULTIPLAYER
TENNIS_SCORES cachedMPScores[TENNIS_PLAYERS] // cached these for earlier calculations for walking to position, can't rely on server data in time
INT iCachedCurrentSet // Cached because the set from the server can change before we've done our game/set won message calculations
TENNIS_BALL lerpBall
#ENDIF
#IF IS_DEBUG_BUILD
TENNIS_WIDGET_DATA tennisWidgets
#ENDIF
ENDSTRUCT
STRUCT TENNIS_GAME_STATUS
TENNIS_PLAYER_ID eWhoIsServing
TENNIS_PLAYER_ID eHitLast
TENNIS_PLAYER_ID eScoredLast
SERVING_SIDE_ENUM eServingSide = SERVING_FROM_RIGHT
TENNIS_GAME_STATE_ENUM eGameState
TENNIS_GAME_STATE_ENUM ePrevGameState
TENNIS_GAME_STATE_ENUM ePausedFromState
TENNIS_SCORES playerScores[TENNIS_PLAYERS]
TENNIS_MATCH_RULESET eRuleset
BOOL bRematch = FALSE
BOOL bTieBreakOn
INT iAIWait
INT iNumSets = 1
INT iServeCount
INT iCurrentSet
FLOAT fPointTimer
ENDSTRUCT
STRUCT TENNIS_GAME_SP_DATA
//Single
AI_DIFFICULTY eDifficulty = AD_EASY
VECTOR vQuitPoint
OBJECT_INDEX oCollBox[4]
PED_VARIATION_STRUCT sVariations
VEHICLE_SAVE_ARGS playerVehicle
FLOAT fAbuseTimer
SCENARIO_BLOCKING_INDEX blockingArea[4]
VECTOR vBlockingArea[6]
VECTOR vNonCreationArea[2]
OBJECT_INDEX oLighting
TENNIS_SP_FLAGS eSPFlags
PED_INDEX pedAbuser
PED_VARIATION_STRUCT sOppVariations
OBJECT_INDEX oBottle
ENDSTRUCT
STRUCT TENNIS_GAME_UI_DATA
TENNIS_CAMERAS tennisCameras
TENNIS_UI tennisUI
UI_STATE_ENUM eUIState
UI_STATE_ENUM ePrevUIState
enumCharacterList playerChars[TENNIS_PLAYERS]
TENNIS_PLAYER_ID eInterstitialPlayer
STRING playerNames[TENNIS_PLAYERS]
VECTOR vInterstitialOffset
// menu selections
INT iRulesSelection = 3
INT iDiffSelection = 1
INT iCurrentOption = 0
// conversation struct
structPedsForConversation myDialogueStruct
// bitflags
INT iUtilFlags
INT iPrevUtilFlags
TENNIS_CONVO_FLAGS eConvoFlags
INT iBS // Used for Conor's offline SCLB screen
// in-game counters
INT iDeuceCount = 1
INT iRallyCounter
INT iNumWalksSinceCut
// cached sync scene ID
INT iSceneID
// Tracking the anims played for peds, got complicated because we have to track each happy and sad anim for both players
INT iLastPlayerHappyAnim
INT iLastAIHappyAnim
INT iUsedPlayerHappyAnims[NUM_PLAYER_ANIMS]
INT iUsedAIHappyAnims[NUM_AI_ANIMS]
INT iLastPlayerSadAnim
INT iLastAISadAnim
INT iUsedPlayerSadAnims[NUM_PLAYER_ANIMS]
INT iUsedAISadAnims[NUM_AI_ANIMS]
// frame/time stamps
INT iQuitWaitStamp
INT iLongRallyConvoStamp
// used for interstitials and cutscenes
STRING sSubtitleGroupID
TEXT_LABEL_63 sInterstitialDict
TEXT_LABEL_31 sInterstitialPlrAnim
TEXT_LABEL_31 sInterstitialCamAnim
TEXT_LABEL_63 sInterstitialFaceDict
TEXT_LABEL_31 sInterstitialFaceAnim
// Timers
FLOAT fMessageTimer
FLOAT fInterstitialTime
structTimer interstitialTimer
structTimer trackingTimer // Currently deprecated
// (x, y) of the swing meter
FLOAT fSwingMeterX
FLOAT fSwingMeterY
// Camera offsets
FLOAT fFarCamBaseOffset
FLOAT fFarCamOtherSideBaseOffset
#IF IS_TENNIS_MULTIPLAYER
PEDHEADSHOT_ID myPedHeadshots[TENNIS_PLAYERS]
BOOL bHeadshotStarted[TENNIS_PLAYERS]
BOOL bHeadshotRegistered[TENNIS_PLAYERS]
CELEBRATION_SCREEN_DATA sCelebData
GENERIC_MINIGAME_STAGE eGenericMGStage
#ENDIF
ENDSTRUCT
STRUCT TENNISMP_LOADING_SPINNER
SCALEFORM_INDEX spinnerMovie
SCALEFORM_LOADING_ICON loadingStruct
ENDSTRUCT
/// ******************************************************* FUNCTIONS ***************************************************************************
#IF IS_TENNIS_MULTIPLAYER
PROC SET_TENNIS_MP_PROPS_CACHED_SET(TENNIS_GAME_PROPERTIES &thisProps, INT iCachedValue)
CDEBUG3LN(DEBUG_TENNIS, "SET_TENNIS_MP_PROPS_CACHED_SET :: iCachedValue=", iCachedValue)
thisProps.iCachedCurrentSet = iCachedValue
ENDPROC
FUNC INT GET_TENNIS_MP_PROPS_CACHED_SET(TENNIS_GAME_PROPERTIES &thisProps)
RETURN thisProps.iCachedCurrentSet
ENDFUNC
#ENDIF
PROC SET_TENNIS_PROPS_DELAY_NEXT_UI_TIME_STAMP(TENNIS_GAME_PROPERTIES &thisProps, INT iNewStamp)
CDEBUG2LN(DEBUG_TENNIS, "SET_TENNIS_PROPS_DELAY_NEXT_UI_TIME_STAMP :: iNewStamp=", iNewStamp)
thisProps.iDelayNextUITimeStamp = iNewStamp
ENDPROC
FUNC INT GET_TENNIS_PROPS_DELAY_NEXT_UI_TIME_STAMP(TENNIS_GAME_PROPERTIES &thisProps)
RETURN thisProps.iDelayNextUITimeStamp
ENDFUNC
PROC SET_TENNIS_STATUS_NUMBER_OF_SETS(TENNIS_GAME_STATUS &thisStatus, INT iNewSets)
CPRINTLN(DEBUG_TENNIS, "SET_TENNIS_STATUS_NUMBER_OF_SETS :: ", thisStatus.iNumSets)
thisStatus.iNumSets = iNewSets
ENDPROC
FUNC INT GET_TENNIS_STATUS_NUM_SETS(TENNIS_GAME_STATUS &thisStatus)
RETURN thisStatus.iNumSets
ENDFUNC
PROC SET_TENNIS_LONG_RALLY_CONVO_STAMP(TENNIS_GAME_UI_DATA &thisUIData, INT iNewStamp)
thisUIData.iLongRallyConvoStamp = iNewStamp
ENDPROC
FUNC INT GET_TENNIS_LONG_RALLY_CONVO_STAMP(TENNIS_GAME_UI_DATA &thisUIData)
RETURN thisUIData.iLongRallyConvoStamp
ENDFUNC
PROC SET_TENNIS_PROPS_BALL_RESET_STAMP(TENNIS_GAME_PROPERTIES &thisProps, INT iNewStamp)
thisProps.iBallResetStamp = iNewStamp
ENDPROC
FUNC INT GET_TENNIS_PROPS_BALL_RESET_STAMP(TENNIS_GAME_PROPERTIES &thisProps)
RETURN thisProps.iBallResetStamp
ENDFUNC
PROC SET_TENNIS_FLOW_FLAG(TENNIS_GAME_PROPERTIES &thisProps, TENNIS_FLOW_FLAGS eFlag)
CDEBUG2LN(DEBUG_TENNIS, "SET_TENNIS_FLOW_FLAG :: Setting ", GET_STRING_FROM_TENNIS_FLOW_FLAGS(eFlag))
thisProps.eFlowFlags = thisProps.eFlowFlags | eFlag
ENDPROC
PROC CLEAR_TENNIS_FLOW_FLAG(TENNIS_GAME_PROPERTIES &thisProps, TENNIS_FLOW_FLAGS eFlag)
CDEBUG2LN(DEBUG_TENNIS, "CLEAR_TENNIS_FLOW_FLAG :: Clearing ", GET_STRING_FROM_TENNIS_FLOW_FLAGS(eFlag))
thisProps.eFlowFlags -= thisProps.eFlowFlags & eFlag
ENDPROC
FUNC BOOL IS_TENNIS_FLOW_FLAG_SET(TENNIS_GAME_PROPERTIES &thisProps, TENNIS_FLOW_FLAGS eFlag)
RETURN (ENUM_TO_INT(thisProps.eFlowFlags) & ENUM_TO_INT(eFlag)) <> 0
ENDFUNC
PROC SET_TENNIS_CONVO_FLAG(TENNIS_GAME_UI_DATA &thisUIData, TENNIS_CONVO_FLAGS eFlag)
CDEBUG2LN(DEBUG_TENNIS, "SET_TENNIS_CONVO_FLAG :: Setting ", GET_STRING_FROM_TENNIS_CONVO_FLAG(eFlag))
thisUIData.eConvoFlags = thisUIData.eConvoFlags | eFlag
ENDPROC
PROC CLEAR_TENNIS_CONVO_FLAG(TENNIS_GAME_UI_DATA &thisUIData, TENNIS_CONVO_FLAGS eFlag)
CDEBUG2LN(DEBUG_TENNIS, "CLEAR_TENNIS_CONVO_FLAG :: Clearing ", GET_STRING_FROM_TENNIS_CONVO_FLAG(eFlag))
thisUIData.eConvoFlags -= thisUIData.eConvoFlags & eFlag
ENDPROC
FUNC BOOL IS_TENNIS_CONVO_FLAG_SET(TENNIS_GAME_UI_DATA &thisUIData, TENNIS_CONVO_FLAGS eFlag)
RETURN (ENUM_TO_INT(thisUIData.eConvoFlags) & ENUM_TO_INT(eFlag)) <> 0
ENDFUNC
PROC SET_TENNIS_QUIT_WAIT_STAMP(TENNIS_GAME_UI_DATA &thisUIData, INT iNewStamp)
thisUIData.iQuitWaitStamp = iNewStamp
ENDPROC
FUNC INT GET_TENNIS_QUIT_WAIT_STAMP(TENNIS_GAME_UI_DATA &thisUIData)
RETURN thisUIData.iQuitWaitStamp
ENDFUNC
PROC SET_TENNIS_SP_FLAG(TENNIS_GAME_SP_DATA &thisSPData, TENNIS_SP_FLAGS eFlag)
CDEBUG2LN(DEBUG_TENNIS, "SET_TENNIS_SP_FLAG :: Setting ", GET_STRING_FROM_TENNIS_SP_FLAG(eFlag))
thisSPData.eSPFlags = thisSPData.eSPFlags | eFlag
ENDPROC
PROC CLEAR_TENNIS_SP_FLAG(TENNIS_GAME_SP_DATA &thisSPData, TENNIS_SP_FLAGS eFlag)
CDEBUG2LN(DEBUG_TENNIS, "CLEAR_TENNIS_SP_FLAG :: Clearing ", GET_STRING_FROM_TENNIS_SP_FLAG(eFlag))
thisSPData.eSPFlags -= thisSPData.eSPFlags & eFlag
ENDPROC
FUNC BOOL IS_TENNIS_SP_FLAG_SET(TENNIS_GAME_SP_DATA &thisSPData, TENNIS_SP_FLAGS eFlag)
RETURN (ENUM_TO_INT(thisSPData.eSPFlags) & ENUM_TO_INT(eFlag)) <> 0
ENDFUNC
PROC SET_TENNIS_RALLY_COUNTER(TENNIS_GAME_UI_DATA &thisUIData, INT iNewCount)
CPRINTLN(DEBUG_TENNIS, "SET_TENNIS_RALLY_COUNTER(", iNewCount, ")")
thisUIData.iRallyCounter = iNewCount
ENDPROC
FUNC INT GET_TENNIS_RALLY_COUNTER(TENNIS_GAME_UI_DATA &thisUIData)
RETURN thisUIData.iRallyCounter
ENDFUNC
PROC SET_TENNIS_TIMER_A(TENNIS_GAME_PROPERTIES &thisProps, INT i)
CPRINTLN(DEBUG_TENNIS, "SET_TENNIS_TIMER_A :: thisProps.iTimerA(", i, ")")
thisProps.iTimerA = i
ENDPROC
/// PURPOSE:
/// Returns in ms
/// PARAMS:
/// thisProps -
/// RETURNS:
///
FUNC INT GET_TENNIS_TIMER_A(TENNIS_GAME_PROPERTIES &thisProps)
RETURN thisProps.iTimerA
ENDFUNC
/// PURPOSE:
/// Updates in ms
/// PARAMS:
/// thisProps -
PROC UPDATE_TENNIS_TIMER_A(TENNIS_GAME_PROPERTIES &thisProps)
thisProps.iTimerA += ROUND(GET_FRAME_TIME() * 1000)
ENDPROC
PROC SET_TENNIS_STATUS_REMATCH(TENNIS_GAME_STATUS &thisStatus, BOOL bIsRematch)
thisStatus.bRematch = bIsRematch
ENDPROC
FUNC BOOL GET_TENNIS_STATUS_REMATCH(TENNIS_GAME_STATUS &thisStatus)
RETURN thisStatus.bRematch
ENDFUNC
PROC SET_TENNIS_STATUS_POINT_TIMER(TENNIS_GAME_STATUS &thisStatus, FLOAT fVal = 0.0)
thisStatus.fPointTimer = fVal
ENDPROC
FUNC FLOAT GET_TENNIS_STATUS_POINT_TIMER(TENNIS_GAME_STATUS &thisStatus)
RETURN thisStatus.fPointTimer
ENDFUNC
/// PURPOSE:
/// increments thisStatus.fPointTimer by GET_FRAME_TIME
/// PARAMS:
/// thisStatus -
PROC UPDATE_TENNIS_STATUS_POINT_TIMER(TENNIS_GAME_STATUS &thisStatus)
thisStatus.fPointTimer += GET_FRAME_TIME()
ENDPROC
PROC INCREASE_TENNIS_ABUSE_TIMER(TENNIS_GAME_SP_DATA &thisSPData)
thisSPData.fAbuseTimer += GET_FRAME_TIME()
ENDPROC
FUNC FLOAT GET_TENNIS_ABUSE_TIMER(TENNIS_GAME_SP_DATA &thisSPData)
RETURN thisSPData.fAbuseTimer
ENDFUNC
PROC RESET_TENNIS_ABUSE_TIMER(TENNIS_GAME_SP_DATA &thisSPData)
CDEBUG2LN(DEBUG_TENNIS, "RESET_TENNIS_ABUSE_TIMER timerWas=", thisSPData.fAbuseTimer)
thisSPData.fAbuseTimer = 0
ENDPROC
PROC SET_TENNIS_SET_TIE_BREAK(TENNIS_GAME_STATUS &thisStatus, BOOL bOnTieBreak)
CDEBUG2LN(DEBUG_TENNIS, "SET_TENNIS_SET_TIE_BREAK :: bOnTieBreak=", PICK_STRING(bOnTieBreak, "TRUE", "FALSE"))
thisStatus.bTieBreakOn = bOnTieBreak
ENDPROC
FUNC BOOL IS_TENNIS_SET_ON_TIE_BREAK(TENNIS_GAME_STATUS &thisStatus)
RETURN thisStatus.bTieBreakOn
ENDFUNC
PROC SET_TENNIS_MATCH_RULESET(TENNIS_GAME_STATUS &thisStatus, TENNIS_MATCH_RULESET eNewRuleset)
thisStatus.eRuleset = eNewRuleset
ENDPROC
FUNC TENNIS_MATCH_RULESET GET_TENNIS_MATCH_RULESET(TENNIS_GAME_STATUS &thisStatus)
RETURN thisStatus.eRuleset
ENDFUNC
FUNC INT GET_TENNIS_STATUS_CURRENT_SET(TENNIS_GAME_STATUS &thisStatus)
RETURN thisStatus.iCurrentSet
ENDFUNC
PROC SET_TENNIS_INTERSTITIAL_PLAYER(TENNIS_GAME_UI_DATA &thisUIData, TENNIS_PLAYER_ID eNewPlayer)
CPRINTLN(DEBUG_TENNIS, "SET_TENNIS_INTERSTITIAL_PLAYER :: setting to ", GET_STRING_FROM_TENNIS_PLAYER_ID(eNewPlayer))
thisUIData.eInterstitialPlayer = eNewPlayer
ENDPROC
FUNC TENNIS_PLAYER_ID GET_TENNIS_INTERSTITIAL_PLAYER(TENNIS_GAME_UI_DATA &thisUIData)
RETURN thisUIData.eInterstitialPlayer
ENDFUNC
PROC INCREASE_TENNIS_FRAME_COUNTER(INT &iFrameCounter)
iFrameCounter++
ENDPROC
FUNC INT GET_TENNIS_FRAME_COUNTER(TENNIS_GAME_PROPERTIES &thisProps)
RETURN thisProps.iFrameCounter
ENDFUNC
PROC SET_TENNIS_STATUS_WHO_IS_SERVING(TENNIS_GAME_STATUS &thisStatus, TENNIS_PLAYER_ID eNewServer)
thisStatus.eWhoIsServing = eNewServer
ENDPROC
FUNC TENNIS_PLAYER_ID GET_TENNIS_STATUS_WHO_IS_SERVING(TENNIS_GAME_STATUS &thisStatus)
RETURN thisStatus.eWhoIsServing
ENDFUNC
/// PURPOSE:
/// Pass in the playerScores you want to increase the score of.
/// PARAMS:
/// scores -
/// iUtilFlags -
PROC INCREASE_TENNIS_POINTS_WON(TENNIS_SCORES &scores, INT &iUtilFlags)
#IF IS_DEBUG_BUILD
IF NOT DO_NOT_TRACK_SCORE
#ENDIF
scores.iPointsWon++
SET_BITMASK_AS_ENUM(iUtilFlags, TD_UPDATE_SCORES)
#IF IS_DEBUG_BUILD
ENDIF
#ENDIF
ENDPROC
/// PURPOSE:
/// Pass in the struct for both players as an array, this will reset the points won
/// PARAMS:
/// scores -
/// eSelfID -
/// iUtilFlags -
PROC RESET_TENNIS_POINTS_WON(TENNIS_SCORES &scores[], INT &iUtilFlags)
scores[0].iPointsWon = 0
scores[1].iPointsWon = 0
SET_BITMASK_AS_ENUM(iUtilFlags, TD_UPDATE_SCORES)
ENDPROC
/// PURPOSE:
/// mutator for the player who hit the ball last
/// PARAMS:
/// thisStatus -
/// playerID -
PROC SET_TENNIS_STATUS_HIT_LAST(TENNIS_GAME_STATUS &thisStatus, TENNIS_PLAYER_ID playerID)
CPRINTLN(DEBUG_TENNIS, "SET_TENNIS_STATUS_HIT_LAST: Setting Stroke Flags, ", GET_STRING_FROM_TENNIS_PLAYER_ID(playerID), " just hit the ball.")
thisStatus.eHitLast = playerID
ENDPROC
/// PURPOSE:
/// accessor for the player who hit the ball last
/// PARAMS:
/// thisStatus -
/// RETURNS:
///
FUNC TENNIS_PLAYER_ID GET_TENNIS_STATUS_HIT_LAST(TENNIS_GAME_STATUS &thisStatus)
RETURN thisStatus.eHitLast
ENDFUNC
/// PURPOSE:
/// mutator for the delay given to ambient AI games
/// prevents ambient games from being exactly in sync when they start
/// PARAMS:
/// thisStatus -
/// iNewWait -
PROC SET_TENNIS_STATUS_AI_WAIT(TENNIS_GAME_STATUS &thisStatus, INT iNewWait)
thisStatus.iAIWait = iNewWait
CPRINTLN(DEBUG_TENNIS, "AI Wait time set to: ", iNewWait)
ENDPROC
/// PURPOSE:
/// accessor for the delay given to ambient AI games
/// prevents ambient games from being exactly in sync when they start
/// PARAMS:
/// thisStatus -
/// RETURNS:
///
FUNC INT GET_TENNIS_STATUS_AI_WAIT(TENNIS_GAME_STATUS &thisStatus)
RETURN thisStatus.iAIWait
ENDFUNC
/// PURPOSE:
/// mutator for the player's name
/// PARAMS:
/// thisUIData -
/// eSelfID - The ID in the player array, either eSelfID or eOtherID
/// newName -
PROC SET_TENNIS_PLAYER_NAME(TENNIS_GAME_UI_DATA &thisUIData, TENNIS_PLAYER_ID eSelfID, STRING newName)
thisUIData.playerNames[eSelfID] = newName
ENDPROC
/// PURPOSE:
/// accessor for player's name
/// PARAMS:
/// thisUIData -
/// ePlayerID -
/// RETURNS:
///
FUNC STRING GET_TENNIS_PLAYER_NAME(TENNIS_GAME_UI_DATA &thisUIData, TENNIS_PLAYER_ID ePlayerID)
RETURN thisUIData.playerNames[ePlayerID]
ENDFUNC
/// PURPOSE:
/// Call GET_TENNIS_PED_VARIATIONS before using this function
/// PARAMS:
/// ped - the ped index of the tennis player you're outfitting
/// thisChar - the character enum for the ped above
PROC SET_TENNIS_OUTFIT(PED_INDEX ped, enumCharacterList thisChar)
//Change player ped into tennis outfit here.
IF thisChar = CHAR_MICHAEL
SET_PED_COMP_ITEM_CURRENT_SP(ped, COMP_TYPE_OUTFIT, OUTFIT_P0_TENNIS, FALSE)
CPRINTLN(DEBUG_TENNIS, "SET_TENNIS_OUTFIT Michael")
ELIF thisChar = CHAR_TREVOR
SET_PED_COMP_ITEM_CURRENT_SP(ped, COMP_TYPE_OUTFIT, OUTFIT_P2_TENNIS, FALSE)
CPRINTLN(DEBUG_TENNIS, "SET_TENNIS_OUTFIT Trevor")
ELIF thisChar = CHAR_LAMAR
CPRINTLN(DEBUG_TENNIS, "SET_TENNIS_OUTFIT Lamar")
SET_PED_COMPONENT_VARIATION(ped, PED_COMP_TORSO, 1, 0)
SET_PED_COMPONENT_VARIATION(ped, PED_COMP_LEG, 1, 0)
ELIF thisChar = CHAR_AMANDA
CPRINTLN(DEBUG_TENNIS, "SET_TENNIS_OUTFIT Amanda")
SET_PED_COMPONENT_VARIATION(ped, INT_TO_ENUM(PED_COMPONENT,0), 0, 0)
SET_PED_COMPONENT_VARIATION(ped, INT_TO_ENUM(PED_COMPONENT,1), 0, 0)
SET_PED_COMPONENT_VARIATION(ped, INT_TO_ENUM(PED_COMPONENT,2), 0, 0)
SET_PED_COMPONENT_VARIATION(ped, INT_TO_ENUM(PED_COMPONENT,3), 0, 0)
SET_PED_COMPONENT_VARIATION(ped, INT_TO_ENUM(PED_COMPONENT,4), 0, 0)
SET_PED_COMPONENT_VARIATION(ped, INT_TO_ENUM(PED_COMPONENT,5), 0, 0)
SET_PED_COMPONENT_VARIATION(ped, INT_TO_ENUM(PED_COMPONENT,7), 0, 0)
SET_PED_COMPONENT_VARIATION(ped, INT_TO_ENUM(PED_COMPONENT,8), 0, 0)
SET_PED_COMPONENT_VARIATION(ped, INT_TO_ENUM(PED_COMPONENT,10), 0, 0)
ELIF thisChar = CHAR_JIMMY
CPRINTLN(DEBUG_TENNIS, "SET_TENNIS_OUTFIT Jimmy")
SET_PED_COMPONENT_VARIATION(ped, PED_COMP_BERD, 1, 0)
SET_PED_COMPONENT_VARIATION(ped, PED_COMP_TORSO, 5, 1)
SET_PED_COMPONENT_VARIATION(ped, PED_COMP_LEG, 3, 1)
SET_PED_COMPONENT_VARIATION(ped, PED_COMP_DECL, 1, 0)
ELSE
CPRINTLN(DEBUG_TENNIS, "SET_TENNIS_OUTFIT :: Ped is not Michael, Trevor, Franklin, Amanda, or Lamar. Not changing outfit.")
ENDIF
ENDPROC
PROC SET_TENNIS_SCORED_LAST(TENNIS_GAME_STATUS &thisStatus, TENNIS_PLAYER_ID whoScored)
thisStatus.eScoredLast = whoScored
CPRINTLN(DEBUG_TENNIS, "Setting Scored Last to ", PICK_STRING(thisStatus.eScoredLast = TENNIS_PLAYER_AWAY, "TENNIS_PLAYER_AWAY", "TENNIS_PLAYER_HOME"))
ENDPROC
FUNC TENNIS_PLAYER_ID GET_TENNIS_STATUS_SCORED_LAST(TENNIS_GAME_STATUS &thisStatus)
RETURN thisStatus.eScoredLast
ENDFUNC
PROC SET_TENNIS_SERVING_SIDE(TENNIS_GAME_STATUS &thisStatus, SERVING_SIDE_ENUM newSide)
CDEBUG1LN(DEBUG_TENNIS, "SET_TENNIS_SERVING_SIDE: Setting to ", ENUM_TO_INT(newSide))
thisStatus.eServingSide = newSide
ENDPROC
FUNC SERVING_SIDE_ENUM GET_TENNIS_SERVING_SIDE(TENNIS_GAME_STATUS &thisStatus)
RETURN thisStatus.eServingSide
ENDFUNC
FUNC FLOAT GET_TENNIS_REACT_ANIM_TIME(STRING sReactAnim)
CPRINTLN(DEBUG_TENNIS, "GET_TENNIS_REACT_ANIM_TIME :: ", sReactAnim)
IF ARE_STRINGS_EQUAL(sReactAnim, "react_lose_01") RETURN (125 * 0.033) ENDIF
IF ARE_STRINGS_EQUAL(sReactAnim, "react_lose_02") RETURN (155 * 0.033) ENDIF
IF ARE_STRINGS_EQUAL(sReactAnim, "react_lose_03") RETURN (135 * 0.033) ENDIF
IF ARE_STRINGS_EQUAL(sReactAnim, "react_lose_04") RETURN (195 * 0.033) ENDIF
IF ARE_STRINGS_EQUAL(sReactAnim, "react_lose_05") RETURN (135 * 0.033) ENDIF
IF ARE_STRINGS_EQUAL(sReactAnim, "react_win_01") RETURN (114 * 0.033) ENDIF
IF ARE_STRINGS_EQUAL(sReactAnim, "react_win_02") RETURN (144 * 0.033) ENDIF
IF ARE_STRINGS_EQUAL(sReactAnim, "react_win_03") RETURN (155 * 0.033) ENDIF
IF ARE_STRINGS_EQUAL(sReactAnim, "react_win_04") RETURN (139 * 0.033) ENDIF
IF ARE_STRINGS_EQUAL(sReactAnim, "react_win_05") RETURN (150 * 0.033) ENDIF
CPRINTLN(DEBUG_TENNIS, "GET_TENNIS_REACT_ANIM_TIME :: Returning ANIM_PLAYING_TIME")
RETURN ANIM_PLAYING_TIME
ENDFUNC
PROC RESET_TENNIS_TIMER_AND_INTERSTITIAL_TIME(TENNIS_GAME_UI_DATA &thisUIData, FLOAT newTime)
CPRINTLN(DEBUG_TENNIS, "RESET_TENNIS_TIMER_AND_INTERSTITIAL_TIME newTime=", newTime)
RESTART_TIMER_NOW(thisUIData.interstitialTimer)
thisUIData.fInterstitialTime = newTime
ENDPROC
PROC SET_TENNIS_ANIM_REACTION_FLAGS(INT &iUtilFlags, TENNIS_UTILS thisBitmask)
CLEAR_BITMASK_AS_ENUM(iUtilFlags, TD_PLR_HAPPY)
CLEAR_BITMASK_AS_ENUM(iUtilFlags, TD_PLR_SAD)
CLEAR_BITMASK_AS_ENUM(iUtilFlags, TD_OPP_HAPPY)
CLEAR_BITMASK_AS_ENUM(iUtilFlags, TD_OPP_SAD)
SET_BITMASK_AS_ENUM(iUtilFlags, thisBitmask)
CPRINTLN(DEBUG_TENNIS, "SET_TENNIS_ANIM_REACTION_FLAGS: Turning on thisBitmask: ",
PICK_STRING(thisBitmask = TD_PLR_HAPPY, "TD_PLR_HAPPY",
PICK_STRING(thisBitmask = TD_PLR_SAD, "TD_PLR_SAD",
PICK_STRING(thisBitmask = TD_OPP_HAPPY, "TD_OPP_HAPPY",
PICK_STRING(thisBitmask = TD_OPP_SAD, "TD_OPP_SAD", "bitmask is invalid")))))
ENDPROC
PROC SET_TENNIS_PLAYER_NAME_OFF_ENUM(TENNIS_GAME_UI_DATA &thisUIData, TENNIS_PLAYER_ID eSelfID)
SWITCH thisUIData.playerChars[eSelfID]
CASE CHAR_MICHAEL
SET_TENNIS_PLAYER_NAME(thisUIData, eSelfID, "MICHAEL")
BREAK
CASE CHAR_FRANKLIN
SET_TENNIS_PLAYER_NAME(thisUIData, eSelfID, "FRANKLIN")
BREAK
CASE CHAR_TREVOR
SET_TENNIS_PLAYER_NAME(thisUIData, eSelfID, "TREVOR")
BREAK
CASE CHAR_LAMAR
SET_TENNIS_PLAYER_NAME(thisUIData, eSelfID, "LAMAR")
BREAK
CASE CHAR_AMANDA
SET_TENNIS_PLAYER_NAME(thisUIData, eSelfID, "AMANDA")
BREAK
CASE CHAR_JIMMY
SET_TENNIS_PLAYER_NAME(thisUIData, eSelfID, "JIMMY")
BREAK
DEFAULT
SET_TENNIS_PLAYER_NAME(thisUIData, eSelfID, "TENNIS_OPP")
BREAK
ENDSWITCH
ENDPROC
FUNC TENNIS_GAME_STATE_ENUM GET_TENNIS_GAME_STATE(TENNIS_GAME_STATUS &thisStatus)
RETURN thisStatus.eGameState
ENDFUNC
/// PURPOSE:
/// Sets the current game state and stores the previous state
/// Do not use this accessor when moving to TGSE_QUIT_CONFIRM, use SET_TENNIS_GAME_STATE_QUIT
/// PARAMS:
/// thisStatus -
/// newTGSE -
PROC SET_TENNIS_GAME_STATE(TENNIS_GAME_STATUS &thisStatus, TENNIS_GAME_STATE_ENUM newTGSE)
IF newTGSE = TGSE_QUIT_CONFIRM OR GET_TENNIS_GAME_STATE(thisStatus) = TGSE_QUIT_CONFIRM
SCRIPT_ASSERT("Do not use SET_TENNIS_GAME_STATE to enter or exit the quit state, use SET_TENNIS_GAME_STATE_QUIT or RETURN_FROM_TENNIS_QUIT_STATE instead")
ELIF GET_TENNIS_GAME_STATE(thisStatus) <> newTGSE
thisStatus.ePrevGameState = GET_TENNIS_GAME_STATE(thisStatus)
thisStatus.eGameState = newTGSE
CPRINTLN(DEBUG_TENNIS, "SET_TENNIS_GAME_STATE Previous State=", GET_STRING_FROM_TENNIS_GAME_STATE(thisStatus.ePrevGameState), ", new state=", GET_STRING_FROM_TENNIS_GAME_STATE(newTGSE))
ELSE
CPRINTLN(DEBUG_TENNIS, "SET_TENNIS_GAME_STATE Trying to set state to same state=", GET_STRING_FROM_TENNIS_GAME_STATE(newTGSE))
ENDIF
ENDPROC
/// PURPOSE:
/// Used when entering the quit state, does not alter the previous game state variable as that is used for important tracking calculations
/// PARAMS:
/// thisStatus -
PROC SET_TENNIS_GAME_STATE_QUIT(TENNIS_GAME_STATUS &thisStatus)
thisStatus.ePausedFromState = GET_TENNIS_GAME_STATE(thisStatus)
IF thisStatus.ePausedFromState < TGSE_OPTION_FINISHED
thisStatus.eGameState = TGSE_QUIT_FROM_OPTION_MENU
ELSE
thisStatus.eGameState = TGSE_QUIT_CONFIRM
ENDIF
CPRINTLN(DEBUG_TENNIS, "entering quit state")
ENDPROC
/// PURPOSE:
/// Used when returning from TGSE_QUIT_CONFIRM
/// PARAMS:
/// thisStatus -
PROC RETURN_FROM_TENNIS_QUIT_STATE(TENNIS_GAME_STATUS &thisStatus)
thisStatus.eGameState = thisStatus.ePausedFromState
CPRINTLN(DEBUG_TENNIS, "Returning from quit state")
ENDPROC
FUNC UI_STATE_ENUM GET_TENNIS_UI_STATE(TENNIS_GAME_UI_DATA &thisUIData)
RETURN thisUIData.eUIState
ENDFUNC
FUNC STRING GET_STRING_FROM_TENNIS_UI_STATE(UI_STATE_ENUM eUI)
RETURN PICK_STRING(eUI = TENNIS_UI_SET_INIT, "TENNIS_UI_SET_INIT",
PICK_STRING(eUI = TENNIS_UI_SET_SELECT, "TENNIS_UI_SET_SELECT",
PICK_STRING(eUI = TENNIS_UI_SET_CONVO, "TENNIS_UI_SET_CONVO",
PICK_STRING(eUI = TENNIS_UI_DIFF_INIT, "TENNIS_UI_DIFF_INIT",
PICK_STRING(eUI = TENNIS_UI_DIFF_SELECT, "TENNIS_UI_DIFF_SELECT",
PICK_STRING(eUI = TENNIS_UI_REMATCH, "TENNIS_UI_REMATCH",
PICK_STRING(eUI = TENNIS_UI_AIM, "TENNIS_UI_AIM",
PICK_STRING(eUI = TENNIS_UI_SPIN1, "TENNIS_UI_SPIN1",
PICK_STRING(eUI = TENNIS_UI_SPIN2, "TENNIS_UI_SPIN2",
PICK_STRING(eUI = TENNIS_UI_STROKES, "TENNIS_UI_STROKES",
PICK_STRING(eUI = TENNIS_UI_HELP_SERVE, "TENNIS_UI_HELP_SERVE",
PICK_STRING(eUI = TENNIS_UI_PLAYING, "TENNIS_UI_PLAYING",
PICK_STRING(eUI = TENNIS_UI_STRONG_SWING, "TENNIS_UI_STRONG_SWING",
PICK_STRING(eUI = TENNIS_UI_VETERAN, "TENNIS_UI_VETERAN",
PICK_STRING(eUI = TENNIS_UI_SCOREBOARD, "TENNIS_UI_SCOREBOARD",
PICK_STRING(eUI = TENNIS_UI_FAR_AWAY, "TENNIS_UI_FAR_AWAY",
PICK_STRING(eUI = TENNIS_UI_STR_INFO, "TENNIS_UI_STR_INFO",
PICK_STRING(eUI = TENNIS_UI_QUIT, "TENNIS_UI_QUIT",
"UNKNOWN ENUM"))))))))))))))))))
ENDFUNC
PROC SET_TENNIS_UI_STATE(TENNIS_GAME_UI_DATA &thisUIData, UI_STATE_ENUM newUI, BOOL bRecordPrevious=TRUE)
IF GET_TENNIS_UI_STATE(thisUIData) <> newUI
IF bRecordPrevious
thisUIData.ePrevUIState = GET_TENNIS_UI_STATE(thisUIData)
ENDIF
thisUIData.eUIState = newUI
CPRINTLN(DEBUG_TENNIS, "UI State changed to ", GET_STRING_FROM_TENNIS_UI_STATE(newUI))
ENDIF
ENDPROC
PROC SET_TENNIS_SERVE_COUNT(TENNIS_GAME_STATUS &thisStatus, INT newCount)
thisStatus.iServeCount = newCount
ENDPROC
FUNC INT GET_TENNIS_SERVE_COUNT(TENNIS_GAME_STATUS &thisStatus)
RETURN thisStatus.iServeCount
ENDFUNC
/// PURPOSE:
/// used primarily for finding the serv ice positions for MP tennis, could be used/adapted for single player too
/// PARAMS:
/// thisCourt -
/// serveSide -
/// vRight -
/// amIServing -
/// eCourtSide -
/// RETURNS:
///
FUNC VECTOR GET_TENNIS_SERVICE_POSITION(TENNIS_COURT &thisCourt, SERVING_SIDE_ENUM serveSide, VECTOR vCourtRight, VECTOR vCourtForward, BOOL amIServing, TENNIS_PLAYER_ID eCourtSide)
VECTOR vDest, vBase
FLOAT fMult, fUpMult
TENNIS_PLAYER_ID me = eCourtSide
IF amIServing AND serveSide = SERVING_FROM_RIGHT AND me = TENNIS_PLAYER_AWAY
fMult = 0.75
fUpMult = -SERVICE_Y_BASELINE
vBase = thisCourt.vCourtCorners[0]
CPRINTLN(DEBUG_TENNIS, "TENNIS_PLAYER_AWAY is serving, from Right, on Away side")
ELIF amIServing AND serveSide = SERVING_FROM_LEFT AND me = TENNIS_PLAYER_AWAY
fMult = 0.25
fUpMult = -SERVICE_Y_BASELINE
vBase = thisCourt.vCourtCorners[0]
CPRINTLN(DEBUG_TENNIS, "TENNIS_PLAYER_AWAY is serving, from Left, on Away side")
ELIF amIServing AND serveSide = SERVING_FROM_RIGHT AND me = TENNIS_PLAYER_HOME
fMult = 0.25
fUpMult = SERVICE_Y_BASELINE
vBase = thisCourt.vCourtCorners[3]
CPRINTLN(DEBUG_TENNIS, "TENNIS_PLAYER_HOME is serving, from Right, on Home side")
ELIF amIServing AND serveSide = SERVING_FROM_LEFT AND me = TENNIS_PLAYER_HOME
fMult = 0.75
fUpMult = SERVICE_Y_BASELINE
vBase = thisCourt.vCourtCorners[3]
CPRINTLN(DEBUG_TENNIS, "TENNIS_PLAYER_HOME is serving, from Left, on Home side")
ELIF !amIServing AND serveSide = SERVING_FROM_RIGHT AND me = TENNIS_PLAYER_AWAY
fMult = 0.75
fUpMult = 0
vBase = thisCourt.vCourtCorners[0]
CPRINTLN(DEBUG_TENNIS, "TENNIS_PLAYER_AWAY not serving, from Right, on Away side")
ELIF !amIServing AND serveSide = SERVING_FROM_LEFT AND me = TENNIS_PLAYER_AWAY
fMult = 0.25
fUpMult = 0
vBase = thisCourt.vCourtCorners[0]
CPRINTLN(DEBUG_TENNIS, "TENNIS_PLAYER_AWAY not serving, from Left, on Away side")
ELIF !amIServing AND serveSide = SERVING_FROM_RIGHT AND me = TENNIS_PLAYER_HOME
fMult = 0.25
fUpMult = 0
vBase = thisCourt.vCourtCorners[3]
CPRINTLN(DEBUG_TENNIS, "TENNIS_PLAYER_HOME not serving, from Right, on Home side")
ELIF !amIServing AND serveSide = SERVING_FROM_LEFT AND me = TENNIS_PLAYER_HOME
fMult = 0.75
fUpMult = 0
vBase = thisCourt.vCourtCorners[3]
CPRINTLN(DEBUG_TENNIS, "TENNIS_PLAYER_HOME not serving, from Left, on Home side")
ELSE
SCRIPT_ASSERT("We missed a case in GET_TENNIS_SERVICE_POSITION, contact Rob Pearsall")
ENDIF
vDest = vBase + (vCourtRight * fMult) + (vCourtForward * fUpMult)
// #IF NOT IS_TENNIS_MULTIPLAYER
// SET_GRAVITY_LEVEL(GRAV_ZERO)
// #ENDIF
RETURN vDest
ENDFUNC
/// PURPOSE:
/// records the win in the current set and checks to see if the set was won and increases sets won as well
/// iCurrentSet is NOT incremented here tho you might think it should be, that's done in TGSE_CHANGE_SERVERS
/// PARAMS:
/// thisStatus -
/// eSelfID - ID of the winner of the game.
/// eOtherID - Loser of the game
/// iUtilFlags -
/// bOnTieBreak - when TRUE it will mark the set as won, a game won on a tie break will decide a set
PROC RECORD_TENNIS_GAME_WIN(TENNIS_SCORES &playerScores[], INT iCurrentSet, TENNIS_PLAYER_ID eWinnerID, TENNIS_PLAYER_ID eLoserID, INT &iUtilFlags, BOOL bOnTieBreak)
IF playerScores[eWinnerID].iGamesWon[iCurrentSet] = -1
//this case shouldn't hit but this is here in case it does.
playerScores[eWinnerID].iGamesWon[iCurrentSet] = 1
CPRINTLN(DEBUG_TENNIS, "RECORD_TENNIS_GAME_WIN: something wrong occurred, games won in the current set is -1, this should have been primed elsewhere.")
ELSE
playerScores[eWinnerID].iGamesWon[iCurrentSet]++
ENDIF
// Set won?
IF (playerScores[eWinnerID].iGamesWon[iCurrentSet] > 5
AND (playerScores[eWinnerID].iGamesWon[iCurrentSet]) - playerScores[eLoserID].iGamesWon[iCurrentSet] >= 2
AND playerScores[eWinnerID].iPointsWon >= 3
AND NOT bOnTieBreak)
OR (bOnTieBreak)
//end of huge IF statement
playerScores[eWinnerID].iSetsWon++
CPRINTLN(DEBUG_TENNIS, "Set won by ", PICK_STRING((TENNIS_PLAYER_AWAY) = eWinnerID, "TENNIS_PLAYER_AWAY", "TENNIS_PLAYER_HOME"))
ENDIF
SET_BITMASK_AS_ENUM(iUtilFlags, TD_UPDATE_SCORES)
CPRINTLN(DEBUG_TENNIS, "Recording Tennis Game Win for ", PICK_STRING((TENNIS_PLAYER_AWAY) = eWinnerID, "TENNIS_PLAYER_AWAY", "TENNIS_PLAYER_HOME"), " in iCurrentSet: ", iCurrentSet)
ENDPROC
/// PURPOSE:
///
/// PARAMS:
/// playerScores -
/// iCurrentSet - Pass by reference, not a GET_ method
/// iUtilFlags -
PROC INCREASE_CURRENT_TENNIS_SET(TENNIS_SCORES &playerScores[], INT &iCurrentSet, INT &iUtilFlags, INT iMaxSets)
IF iCurrentSet + 1 < iMaxSets
iCurrentSet++
playerScores[0].iPointsWon = 0
playerScores[1].iPointsWon = 0
playerScores[0].iGamesWon[iCurrentSet] = 0
playerScores[1].iGamesWon[iCurrentSet] = 0
CPRINTLN(DEBUG_TENNIS, "actually resetting points in set ", iCurrentSet)
ENDIF
SET_BITMASK_AS_ENUM(iUtilFlags, TD_UPDATE_SCORES)
CPRINTLN(DEBUG_TENNIS, "INCREASE_CURRENT_TENNIS_SET called, points and games reset.")
ENDPROC
PROC SET_TENNIS_CHARACTER_SEEN_TUTORIAL()
SET_TENNIS_SAVED_GLOBAL_FLAG(g_savedGlobals.sTennisData, TGF_STR_INFO_SHOWN)
ENDPROC
PROC CLEAR_TENNIS_CHARACTER_SEEN_TUTORIAL()
CLEAR_TENNIS_SAVED_GLOBAL_FLAG(g_savedGlobals.sTennisData, TGF_STR_INFO_SHOWN)
ENDPROC
FUNC BOOL HAS_TENNIS_CHARACTER_SEEN_TUTORIAL()
RETURN IS_TENNIS_SAVED_GLOBAL_FLAG_SET(g_savedGlobals.sTennisData, TGF_STR_INFO_SHOWN)
ENDFUNC
/// PURPOSE:
/// gates whether or not we use a specific ped for the game.
/// PARAMS:
/// eActivity - the current tennis activity enum
/// RETURNS:
/// true if the tutorial isn't seen and the activity is not a friend activity
FUNC BOOL VALIDATE_PICK_DEFAULT_TENNIS_OPPONENT(TENNIS_ACTIVITY eActivity)
IF eActivity = TA_FRIEND_ACTIVITY
RETURN FALSE
ENDIF
IF HAS_TENNIS_CHARACTER_SEEN_TUTORIAL()
RETURN FALSE
ENDIF
IF IS_TENNIS_GLOBAL_FLAG_SET(TGF_AMANDA_IS_OPP)
RETURN FALSE
ENDIF
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// Wrapper for the timer so we have output.
/// PARAMS:
/// sTimer -
PROC TENNIS_PAUSE_TIMER(structTimer &sTimer)
PAUSE_TIMER(sTimer)
DEBUG_PRINTCALLSTACK()
ENDPROC