/**************************************************************************** * KewlettAPC.SNL * AI LOGIC FOR THE KEWLIT APC (Army Personal Carrier) * PeterM June 14 2002 ****************************************************************************/ #inherit "TurretVehicle.SNL" /**************************************************************************** * SNL GLOBAL VARIABLES ****************************************************************************/ VAR g_CurrentState_DeployTroops; // Current substate in the Deploy troops state VAR g_CurrentState_ReceiveTroops; // Current substate in the Receive troops state VAR g_AIName; // AI Name for the APC VAR g_DestLaunchDoorAngle; // Angle launch door is going towards VAR g_LaunchDoorAngle; // Angle launch door is currently at VAR g_LaunchDoorSpeed; // Speed at which the launch door is moving at VAR g_KewlettStorageBlaster; // Number of belly blaster kewletts that the APC can hold VAR g_KewlettStorageLoliAxe; // Number of loliaxe kewletts that the APC can hold VAR g_NumKewlettDeployedBlaster; // The number of blaster kewletts that have been deployed VAR g_NumKewlettDeployedLoliAxe; // The number of loliaxe kewletts that have been deployed VAR g_LaunchTroopDelay; // The time in between spawning troops from the APC VAR g_CloseDoorDelay; // The time between the last troop deployed and the launch door closing VAR g_IsDoorClosing; // Indicates whether the door is closing or not VAR g_IsDoorOccupied; // Indicates whether a kewlett is entering/exiting the APC VAR g_IsReadyToReceiveTroops; // Indicates if all the troops are ready to enter the APC VAR g_DamageState; // How damaged the vehicle is. Used for generating special effects. VAR g_Damaged0, g_Damaged1; VAR g_Damaged2, g_Damaged3; VAR g_Damaged4, g_Damaged5; VAR g_Damaged6, g_Damaged7; VAR PARAM_KewlettAPC_OpenDoorSoundPool; VAR PARAM_KewlettAPC_OpenDoorSoundMinDistance; VAR PARAM_KewlettAPC_OpenDoorSoundMaxDistance; VAR PARAM_KewlettAPC_CloseDoorSoundPool; VAR PARAM_KewlettAPC_CloseDoorSoundMinDistance; VAR PARAM_KewlettAPC_CloseDoorSoundMaxDistance; /**************************************************************************** * SNL FUNCTION PROTOTYPES *****************************************************************************/ OpenLaunchDoor(); IsLaunchDoorOpen(); CloseLaunchDoor(); IsLaunchDoorClosed(); ProcessLaunchDoorMotion( seconds ); IsLastTroopDeployed(); DeployATroop(); RotateDoor(); OnFinishKewlettAPCExit(); GetKewlettStorageCount(); GetKewlettDeploymentCount(); SetTroopPatrolData( kewlett ); PlayOpenLaunchDoorSound(); PlayCloseLaunchDoorSound(); /**************************************************************************** * SNL STATE PROTOTYPES ****************************************************************************/ // Deploy Troops State Machine SetAIState_DeployTroops( newState ); Begin_STATE_DeployTroops(); HeartBeat_STATE_DeployTroops( seconds ); Begin_DeployTroops_STATE_OpenDoor(); HeartBeat_DeployTroops_STATE_OpenDoor(); Begin_DeployTroops_STATE_LaunchTroops(); HeartBeat_DeployTroops_STATE_LaunchTroops( seconds ); Begin_DeployTroops_STATE_CloseDoor(); HeartBeat_DeployTroops_STATE_CloseDoor( seconds ); // Receive Troops State Machine SetAIState_ReceiveTroops( newState ); Begin_STATE_ReceiveTroops(); HeartBeat_STATE_ReceiveTroops( seconds ); Begin_ReceiveTroops_STATE_OpenDoor(); HeartBeat_ReceiveTroops_STATE_OpenDoor(); Begin_ReceiveTroops_STATE_ReceiveTroops(); HeartBeat_ReceiveTroops_STATE_ReceiveTroops( seconds ); Begin_ReceiveTroops_STATE_CloseDoor(); HeartBeat_ReceiveTroops_STATE_CloseDoor( seconds ); /**************************************************************************** * Called when the object is first created * TimS March 3 2002 ****************************************************************************/ OnCreate() { // Call the base class method ::OnCreate(); // Set object properties SetProperty1( THIS, "Sight_Range", 1000 ); SetProperty1( THIS, "Sight_Radius", 30 ); SetProperty1( THIS, "Hearing", 0 ); SetProperty1( THIS, "Turn_Speed", 3.14159 / 2.0 ); SetProperty1( THIS, "AvoidObstacles", 0 ); SetProperty1( THIS, "MaxHealth", 900 ); SetProperty1( THIS, "Health", 900 ); // Initialize local variables g_KewlettStorageBlaster = 2;//2; g_KewlettStorageLoliAxe = 2;//3; g_DestLaunchDoorAngle = 0; g_LaunchDoorAngle = 0; g_LaunchDoorSpeed = 30; g_LaunchTroopDelay = 0; g_IsReadyToReceiveTroops = 0; // Initialize variables for the Turret Control code PARAM_TurretVehicle_TurretBone = 4; PARAM_TurretVehicle_CannonBone = 5; PARAM_TurretVehicle_MissileObj = "APCMissile.OBL"; PARAM_TurretVehicle_TurretOffsetX = -114; PARAM_TurretVehicle_TurretOffsetY = 94.5; PARAM_TurretVehicle_TurretOffsetZ = -114; PARAM_TurretVehicle_TurretLength = 20; PARAM_TurretVehicle_MissileSpeed = 500; PARAM_TurretVehicle_TurretRotationSpeed = 15; PARAM_TurretVehicle_CannonRotationSpeed = 15; PARAM_TurretVehicle_FireMissleTime = 0.5; PARAM_TurretVehicle_ReloadMissleTime = 5; PARAM_TurretVehicle_NumSmokeParticles = 60; PARAM_TurretVehicle_MaxSmokeParticleSize = 100; PARAM_TurretVehicle_MaxSmokeParticleOffset = 10; PARAM_TurretVehicle_MinFireRange = 120; PARAM_TurretVehicle_MaxFireRange = 1000; PARAM_TurretVehicle_CWSearchBoundary = 270; PARAM_TurretVehicle_CCWSearchBoundary = 90; PARAM_TurretVehicle_StartAngle = 180; // Sound Parameters PARAM_TurretVehicle_TurretRotationSoundPool = "AUDIOPOOL_TURRETROTATION"; PARAM_TurretVehicle_TurretRotationSoundMinDistance = 500; PARAM_TurretVehicle_TurretRotationSoundMaxDistance = 5000; PARAM_TurretVehicle_IdleSoundPool = "AUDIOPOOL_KAPCIDLE"; PARAM_TurretVehicle_IdleSoundMinDistance = 350; PARAM_TurretVehicle_IdleSoundMaxDistance = 5000; PARAM_TurretVehicle_RevUpSoundPool = "AUDIOPOOL_KAPCREVUP"; PARAM_TurretVehicle_RevUpSoundMinDistance = 750; PARAM_TurretVehicle_RevUpSoundMaxDistance = 5000; PARAM_TurretVehicle_RollSoundPool = "AUDIOPOOL_KAPCROLL"; PARAM_TurretVehicle_RollSoundMinDistance = 350; PARAM_TurretVehicle_RollSoundMaxDistance = 5000; PARAM_TurretVehicle_RevDownSoundPool = "AUDIOPOOL_KAPCREVDOWN"; PARAM_TurretVehicle_RevDownSoundMinDistance = 750; PARAM_TurretVehicle_RevDownSoundMaxDistance = 5000; PARAM_TurretVehicle_TurretBlastSoundPool = "AUDIOPOOL_TURRETBLASTS"; PARAM_TurretVehicle_TurretBlastSoundMinDistance = 350; PARAM_TurretVehicle_TurretBlastSoundMaxDistance = 5000; PARAM_TurretVehicle_VehicleExplosionSoundPool = "AUDIOPOOL_MEDIUM_VEHICLEEXPLOSION"; PARAM_TurretVehicle_VehicleExplosionSoundMinDistance = 3000; PARAM_TurretVehicle_VehicleExplosionSoundMaxDistance = 5000; PARAM_KewlettAPC_OpenDoorSoundPool = "AUDIOPOOL_KAPCOPENDOOR"; PARAM_KewlettAPC_OpenDoorSoundMinDistance = 750; PARAM_KewlettAPC_OpenDoorSoundMaxDistance = 5000; PARAM_KewlettAPC_CloseDoorSoundPool = "AUDIOPOOL_KAPCCLOSEDOOR"; PARAM_KewlettAPC_CloseDoorSoundMinDistance = 750; PARAM_KewlettAPC_CloseDoorSoundMaxDistance = 5000; // Set the team SetTeam( THIS, "Kewletts" ); g_TurretAngle = 180; g_DestTurretAngle = 180; g_IsDoorOccupied = 1; g_DamageState = 0; g_Damaged0 = -1; g_Damaged1 = -1; g_Damaged2 = -1; g_Damaged3 = -1; g_Damaged4 = -1; g_Damaged5 = -1; g_Damaged6 = -1; g_Damaged7 = -1; SetProperty1( THIS, "Sight_Height", 110 ); } /***************************************************************************/ /**************************************************************************** * Called when the object is first turned on * TimS March 3 2002 ****************************************************************************/ OnBeginScript() { ::OnBeginScript(); SetAIState( "Idle" ); AddProperty2(THIS, "DoorAngle", g_LaunchDoorAngle, "AI"); AddProperty2(THIS, "DestDoorAngle", g_DestLaunchDoorAngle, "AI"); AddProperty2(THIS, "IsDoorOccupied", g_IsDoorOccupied, "AI"); g_AIName = QueryObjectName(THIS); //HUD_Enable(THIS); // HUD_AddVarWatch(THIS, g_AIName); // HUD_AddVarWatch(THIS, g_CurrentState); // HUD_AddVarWatch(THIS, g_DestCannonAngle); // HUD_AddVarWatch(THIS, g_CannonAngle); //HUD_AddVarWatch(THIS, g_IsReadyToReceiveTroops); //HUD_AddVarWatch(THIS, g_CurrentState_ReceiveTroops); } /***************************************************************************/ /**************************************************************************** * Play the APC Open Door Sound * PeterM July 22 2002 ****************************************************************************/ PlayAPCOpenDoorSound() { // Check to see if we're audible, if not, then return if ( IsAudible(THIS, PARAM_KewlettAPC_OpenDoorSoundMaxDistance) == 0 ) { return; } // Get the turret rotation sound gt_Audio = FX_QueryFreeSoundFromPool(PARAM_KewlettAPC_OpenDoorSoundPool); if (gt_Audio != "") { FX_SetSoundDistances(gt_Audio, PARAM_KewlettAPC_OpenDoorSoundMinDistance, PARAM_KewlettAPC_OpenDoorSoundMaxDistance); FX_PlaySound( THIS, gt_Audio, 1.0, 1 ); } } /***************************************************************************/ /**************************************************************************** * Play the close door apc sound * PeterM July 22 2002 ****************************************************************************/ PlayAPCCloseDoorSound() { // Check to see if we're audible, if not, then return if ( IsAudible(THIS, PARAM_KewlettAPC_CloseDoorSoundMaxDistance) == 0 ) { return; } // Get the turret rotation sound gt_Audio = FX_QueryFreeSoundFromPool(PARAM_KewlettAPC_CloseDoorSoundPool); if (gt_Audio != "") { FX_SetSoundDistances(gt_Audio, PARAM_KewlettAPC_CloseDoorSoundMinDistance, PARAM_KewlettAPC_CloseDoorSoundMaxDistance); FX_PlaySound( THIS, gt_Audio, 1.0, 1 ); } } /***************************************************************************/ /**************************************************************************** * Opens the launch door * PeterM June 19 2002 ****************************************************************************/ OpenLaunchDoor() { // Set the destination angle to be 180 g_DestLaunchDoorAngle = 150; // Play the launch door open sound PlayAPCOpenDoorSound(); } /***************************************************************************/ /**************************************************************************** * Returns whether the launch door is open or not * PeterM June 19 2002 ****************************************************************************/ IsLaunchDoorOpen() { return (g_LaunchDoorAngle == 150); } /***************************************************************************/ /**************************************************************************** * Closes the launch door * PeterM June 19 2002 ****************************************************************************/ CloseLaunchDoor() { // Set the destination angle to be -180 g_DestLaunchDoorAngle = 0; // Play the launch door open sound PlayAPCCloseDoorSound(); } /***************************************************************************/ /**************************************************************************** * Returns whether the launch door is closed or not * PeterM June 19 2002 ****************************************************************************/ IsLaunchDoorClosed() { return (g_LaunchDoorAngle == 0); } /***************************************************************************/ /**************************************************************************** * Rotates the door to a specified angle. * PeterM June 19 2002 ****************************************************************************/ RotateDoor( angle ) { VAR euler_angle; // Convert the fire angle to an euler angle. euler_angle = 180 - angle; FX_RotateBone( THIS, 3, euler_angle, 0, 0 ); } /***************************************************************************/ /**************************************************************************** * Processes the launch door motion * PeterM June 19 2002 ****************************************************************************/ ProcessLaunchDoorMotion( seconds ) { VAR doorTurnSpeed; // Rotate the door if the destination angle is not equal to the current angle. if ( (g_DestLaunchDoorAngle != g_LaunchDoorAngle) ) { doorTurnSpeed = g_LaunchDoorSpeed * seconds; g_LaunchDoorAngle = TurnTowards( g_DestLaunchDoorAngle, g_LaunchDoorAngle, doorTurnSpeed ); RotateDoor( g_LaunchDoorAngle ); } } /***************************************************************************/ /**************************************************************************** * A Utility function to return the number of kewletts that can be stored in * the APC * PeterM June 24 2002 ****************************************************************************/ GetKewlettStorageCount() { return g_KewlettStorageBlaster + g_KewlettStorageLoliAxe; } /***************************************************************************/ /**************************************************************************** * A Utility function to return the number of kewletts that have been deployed * PeterM June 24 2002 ****************************************************************************/ GetKewlettDeploymentCount() { return g_NumKewlettDeployedBlaster + g_NumKewlettDeployedLoliAxe; } /***************************************************************************/ /**************************************************************************** * Returns true or not whether the last troop has been deployed from the APC * PeterM June 19 2002 ****************************************************************************/ IsLastTroopDeployed() { return ( GetKewlettStorageCount() == GetKewlettDeploymentCount() ); } /***************************************************************************/ /**************************************************************************** * Deploys a Troop * PeterM June 19 2002 ****************************************************************************/ DeployATroop() { VAR kewlett; VAR kewlettX, kewlettY, kewlettZ, kewlett_angle; VAR kewlett_type; VAR rand; VAR angle; // Initialize local variables kewlettX = 0; kewlettY = 0; kewlettZ = 0; kewlett_angle = 0; kewlett_type = 0; rand = 0; // If we haven't hit our storage limit yet, keep spawning kewletts out // of the APC if ( GetKewlettDeploymentCount() < GetKewlettStorageCount() ) { // Determine the type of kewlett to deploy. If we run out of // storage for the given type, then just switch to the other type // otherwise randomly pick which one to deploy next. // '0' will indicate the blaster // '1' will indicate the loliaxe if (g_NumKewlettDeployedBlaster == g_KewlettStorageBlaster) { kewlett_type = 1; } else { if (g_NumKewlettDeployedLoliAxe == g_KewlettStorageLoliAxe) { kewlett_type = 0; } else { rand = Random(1, 10); if ( rand < 5 ) { kewlett_type = 0; } else { kewlett_type = 1; } } } // Find the position to the door kewlett_angle = GetAngle(THIS) + 180; kewlettX = GetX(THIS) + 130 * Sin(kewlett_angle); kewlettY = GetY(THIS) + 0; kewlettZ = GetZ(THIS) + 130 * Cos(kewlett_angle); // kewlett_angle = GetAngle(THIS); // DEBUG("APC Pos is "+kewlettX+", "+kewlettY+", "+kewlettZ+""); // FX_QueryBonePos(THIS, 3, kewlettX, kewlettY, kewlettZ); // DEBUG("Bone Pos is "+kewlettX+", "+kewlettY+", "+kewlettZ+""); // Offset it back and down // doorY = doorY - 60; // doorX = doorX - 20; // Create the kewlett if (kewlett_type == 0 ) { kewlett = CreateObject( "OBL/KewlettFlamerGuard.OBL", kewlettX, kewlettY, kewlettZ ); g_NumKewlettDeployedBlaster = g_NumKewlettDeployedBlaster + 1; } else { kewlett = CreateObject( "OBL/KewlettFlamerAxe.OBL", kewlettX, kewlettY, kewlettZ ); g_NumKewlettDeployedLoliAxe = g_NumKewlettDeployedLoliAxe + 1; } SetDirection(kewlett, kewlett_angle); // add kewletts to APC's team if ( g_CaptainID > -1 ) { AddSoliderToTeam(kewlett, g_CaptainID, "APCSpawn", GetKewlettDeploymentCount()); } // DEBUG("Created kewlett object"); CallFunction(kewlett, "OnBeginScript"); // in this case, the PatrolAngle is the angle offset for the exiting the APC angle = 30 + ( -20 * (GetKewlettDeploymentCount() - 1) ); SetProperty1( kewlett, "PatrolAngle", angle ); //SetTroopPatrolData(kewlett); SetAIState2(kewlett, "ExitAPC"); // DEBUG("Set Kewlett object state"); // Reset the delay counter g_LaunchTroopDelay = 1.5; } } /***************************************************************************/ /**************************************************************************** * _STATE_Idle() * Functions to handle idling * TimS June 19 2002 ****************************************************************************/ Begin_STATE_Idle() { // Call the base class method ::Begin_STATE_Idle(); // Play the APC Idle Animation // FX_PlayAnimation( THIS, "GAMEDATA_TAN_KEWLETTIDLEA", 0.25, "", 1.0 ); Halt(THIS); } HeartBeat_STATE_Idle( seconds ) { // Call the base class method ::HeartBeat_STATE_Idle( seconds ); } /***************************************************************************/ /**************************************************************************** * _STATE_DeployTroops() * Functions to handle Launch Troops * TimS June 19 2002 ****************************************************************************/ Begin_DeployTroops_STATE_OpenDoor() { OpenLaunchDoor(); } HeartBeat_DeployTroops_STATE_OpenDoor() { // If the launch door is open, start launching troops if ( IsLaunchDoorOpen() ) { SetAIState_DeployTroops( "Launch Troops" ); } } Begin_DeployTroops_STATE_LaunchTroops() { // Initalize the state variables g_NumKewlettDeployedLoliAxe = 0; g_NumKewlettDeployedBlaster = 0; g_LaunchTroopDelay = 0; } HeartBeat_DeployTroops_STATE_LaunchTroops( seconds ) { // Count down the launch troop delay g_LaunchTroopDelay = g_LaunchTroopDelay - seconds; // If the last kewlit is deployed, close the door // otherwise launch another kewlit if ( IsLastTroopDeployed() ) { SetAIState_DeployTroops( "Close Door" ); } else { if (g_LaunchTroopDelay < 0 ) { DeployATroop(); } } } Begin_DeployTroops_STATE_CloseDoor() { // Close the APC Door g_CloseDoorDelay = 2; g_IsDoorClosing = 0; } HeartBeat_DeployTroops_STATE_CloseDoor( seconds ) { // Wait a bit of delay before issue the door close if ( g_CloseDoorDelay > 0 ) { g_CloseDoorDelay = g_CloseDoorDelay - seconds; } else { // Begin to close the door if (g_IsDoorClosing == 0 ) { CloseLaunchDoor(); g_IsDoorClosing = 1; } // If the launch door is closed, switch back to // the idle state if ( IsLaunchDoorClosed() ) { SetAIState("Idle"); } } } SetAIState_DeployTroops( newState ) { ClearStimuli( THIS ); g_CurrentState_DeployTroops = newState; if ( newState == "Open Door" ) { Begin_DeployTroops_STATE_OpenDoor(); } if ( newState == "Launch Troops" ) { Begin_DeployTroops_STATE_LaunchTroops(); } if ( newState == "Close Door" ) { Begin_DeployTroops_STATE_CloseDoor(); } // DEBUG("APC - Deploy Troops state is "+g_CurrentState_DeployTroops+""); } Begin_STATE_DeployTroops() { // Play the idle sound - two cases we were always in an idle sound so // we don't need to do anything other then play the sound or it is playing // or we're reving down from a roll sound so first stop the roll and play // a rev sound before going to idle if (g_TurretVehicleIdleSound == "") { if ( g_TurretVehicleRollingSound != "") { PlayTurretVehicleRevDownSound(); StopTurretVehicleRollingSound(); } PlayTurretVehicleIdleSound(); } // Open the APC door SetAIState_DeployTroops("Open Door"); } HeartBeat_STATE_DeployTroops( seconds ) { if ( g_CurrentState_DeployTroops == "Open Door" ) { HeartBeat_DeployTroops_STATE_OpenDoor(); } if ( g_CurrentState_DeployTroops == "Launch Troops" ) { HeartBeat_DeployTroops_STATE_LaunchTroops( seconds ); } if ( g_CurrentState_DeployTroops == "Close Door" ) { HeartBeat_DeployTroops_STATE_CloseDoor( seconds ); } // Process the launch door motion ProcessLaunchDoorMotion( seconds ); } /***************************************************************************/ /**************************************************************************** * _STATE_ReceiveTroops() * Functions to handle Receive Troops * DAVIDE July 5 2002 ****************************************************************************/ Begin_ReceiveTroops_STATE_OpenDoor() { OpenLaunchDoor(); g_IsDoorOccupied = 1; } HeartBeat_ReceiveTroops_STATE_OpenDoor() { // If the launch door is open, start launching troops if ( IsLaunchDoorOpen() ) { SetAIState_ReceiveTroops( "Receive Troops" ); } } Begin_ReceiveTroops_STATE_ReceiveTroops() { g_IsDoorOccupied = 0; g_AllPurposeWaitTime = 2; } HeartBeat_ReceiveTroops_STATE_ReceiveTroops( seconds ) { VAR capObj; VAR maxSubs, cnt; VAR subID, subObj; VAR subName; VAR everySpawnIn; g_AllPurposeWaitTime = g_AllPurposeWaitTime - seconds; if( g_AllPurposeWaitTime <= 0 ) { g_AllPurposeWaitTime = 1; // check if there are any spawns left on the team. // If not, then we are done, resume delivery, if that's // what we were suppose to do. capObj = FindObjectByID( g_CaptainID ); maxSubs = QueryNumSubordinates( capObj ); cnt = 0; everySpawnIn = 1; while ( cnt < maxSubs ) { subID = QuerySubordinateID( capObj, cnt ); subObj = FindObjectByID( subID ); subName = QueryObjectName( subObj ); if( LookForString(subName, "SPAWN") == 1 ) { everySpawnIn = 0; cnt = maxSubs; } cnt++; } if( everySpawnIn == 1 ) { SetAIState_ReceiveTroops( "Close Door" ); } } } Begin_ReceiveTroops_STATE_CloseDoor() { // Close the APC Door g_CloseDoorDelay = 0; g_IsDoorClosing = 0; g_IsDoorOccupied = 1; } HeartBeat_ReceiveTroops_STATE_CloseDoor( seconds ) { VAR curState; // Wait a bit of delay before issue the door close if ( g_CloseDoorDelay > 0 ) { g_CloseDoorDelay = g_CloseDoorDelay - seconds; } else { // Begin to close the door if (g_IsDoorClosing == 0 ) { CloseLaunchDoor(); g_IsDoorClosing = 1; } // If the launch door is closed, switch back to // the idle state if ( IsLaunchDoorClosed() ) { SetAIState("Idle"); SendInfoToCaptain( "ResumeDelivery", 0, 0, 0, 0, 0, "" ); } } } SetAIState_ReceiveTroops( newState ) { ClearStimuli( THIS ); g_CurrentState_ReceiveTroops = newState; if ( newState == "Open Door" ) { Begin_ReceiveTroops_STATE_OpenDoor(); } if ( newState == "Receive Troops" ) { Begin_ReceiveTroops_STATE_ReceiveTroops(); } if ( newState == "Close Door" ) { Begin_ReceiveTroops_STATE_CloseDoor(); } } Begin_STATE_ReceiveTroops() { // Open the APC door g_IsReadyToReceiveTroops = 0; SetAIState_ReceiveTroops("Open Door"); g_AllPurposeWaitTime = 0; } HeartBeat_STATE_ReceiveTroops( seconds ) { VAR capObj; VAR maxSubs, cnt; VAR subID, subObj; VAR subName; VAR spawnsLeft, spawnsReadyToEnter; VAR curState; spawnsReadyToEnter = 0; // keep checking to see if the troops are still alive. // If no more troops are left alive, close the door at start // moseying towards the destination. g_AllPurposeWaitTime = g_AllPurposeWaitTime - seconds; if(( g_AllPurposeWaitTime <= 0 ) && ( g_CurrentState_ReceiveTroops != "Close Door" ) ) { g_AllPurposeWaitTime = 1; g_IsReadyToReceiveTroops = 0; capObj = FindObjectByID( g_CaptainID ); maxSubs = QueryNumSubordinates( capObj ); cnt = 0; spawnsLeft = 0; while ( cnt < maxSubs ) { subID = QuerySubordinateID( capObj, cnt ); subObj = FindObjectByID( subID ); subName = QueryObjectName( subObj ); if( LookForString(subName, "SPAWN") == 1 ) { curState = QuerySubordinateState( capObj, QuerySubordinateIndex( capObj, subObj ) ); if ( curState != "Dead" ) { spawnsLeft++; } curState = QueryProperty_String( subObj, "SNL_State" ); if ( curState == "EnterAPC" ) { spawnsReadyToEnter++; } } cnt++; } if( spawnsLeft == 0 ) { SetAIState_ReceiveTroops( "Close Door" ); return; } else { if( spawnsReadyToEnter == spawnsLeft ) { g_IsReadyToReceiveTroops = 1; } } } if (( g_IsReadyToReceiveTroops == 0 ) && ( g_CurrentState_ReceiveTroops != "Close Door" )) { return; } if ( g_CurrentState_ReceiveTroops == "Open Door" ) { HeartBeat_ReceiveTroops_STATE_OpenDoor(); } if ( g_CurrentState_ReceiveTroops == "Receive Troops" ) { HeartBeat_ReceiveTroops_STATE_ReceiveTroops( seconds ); } if ( g_CurrentState_ReceiveTroops == "Close Door" ) { HeartBeat_ReceiveTroops_STATE_CloseDoor( seconds ); } // Process the launch door motion ProcessLaunchDoorMotion( seconds ); } /***************************************************************************/ /**************************************************************************** * OnStimulus() * Called when the creature receives stimulus from the outside world. * TimS June 19 2002 ****************************************************************************/ OnStimulus( stimulus, arg1, arg2, arg3, arg4, arg5 ) { :: OnStimulus( stimulus, arg1, arg2, arg3, arg4, arg5 ); } /***************************************************************************/ /**************************************************************************** * OnReachDestination() * Called once a Kewlett has reached the last waypoint. * TimS June 19 2002 ****************************************************************************/ OnReachDestination() { ::OnReachDestination(); } /***************************************************************************/ /**************************************************************************** * _STATE_DefendPackage() * Functions to handle defending the convoy * DAVIDE June 20 2002 ****************************************************************************/ Begin_STATE_DefendPackage() { Halt(THIS); g_AllPurposeWaitTime = 0; // Close the APC Door is needed g_CloseDoorDelay = 0; g_IsDoorClosing = 0; g_IsDoorOccupied = 1; } HeartBeat_STATE_DefendPackage( seconds ) { VAR capObj; VAR maxSubs, cnt; VAR subID, subObj; VAR subName; VAR spawnsLeft; VAR curState; // keep checking to see if the troops are still alive. // If no more troops are left alive, deploy more troops g_AllPurposeWaitTime = g_AllPurposeWaitTime - seconds; if( g_AllPurposeWaitTime <= 0 ) { g_AllPurposeWaitTime = 2; capObj = FindObjectByID( g_CaptainID ); maxSubs = QueryNumSubordinates( capObj ); cnt = 0; spawnsLeft = 0; while ( cnt < maxSubs ) { subID = QuerySubordinateID( capObj, cnt ); subObj = FindObjectByID( subID ); subName = QueryObjectName( subObj ); if( LookForString(subName, "SPAWN") == 1 ) { curState = QuerySubordinateState( capObj, QuerySubordinateIndex( capObj, subObj ) ); if ( curState != "Dead" ) { spawnsLeft = 1; cnt = maxSubs; } } cnt++; } if( spawnsLeft == 0 ) { SetAIState("Deploy Troops"); return; } else { // close the door is needed // Begin to close the door g_AllPurposeWaitTime = 0; if (g_IsDoorClosing == 0 ) { CloseLaunchDoor(); g_IsDoorClosing = 1; } // If the launch door is closed, switch ProcessLaunchDoorMotion( seconds ); if ( IsLaunchDoorClosed() == 1 ) { SetAIState("Attack Position"); } } } } /***************************************************************************/ /**************************************************************************** * SetTroopPatrolData() * Sets up the patrol path data for the spawned kewlett * DAVIDE July 4 2002 ****************************************************************************/ SetTroopPatrolData( kewlett ) { VAR timeOffset; VAR secondsOffset; VAR curX, curZ, curDir; VAR speed, radius; curX = 0; curZ = 0; curDir = 0; secondsOffset = 1; timeOffset = secondsOffset * (GetKewlettStorageCount()); // determine if the players should be place before or after the current time stamp if( (g_TotalTimeElapsed - timeOffset) < 0) { // start the last guy at 0 and place everyone else in front timeOffset = (GetKewlettDeploymentCount() - 1) * secondsOffset; g_TotalTimeElapsed = timeOffset; SYS_Break(); } else { // place every one behind the APC point timeOffset = (GetKewlettDeploymentCount() - 1) * secondsOffset; g_TotalTimeElapsed = g_TotalTimeElapsed - timeOffset; } // get the new position and other data related to the new time stamp speed = QueryProperty_Number( g_FormationLeader, "Walk_Speed" ); radius = QueryProperty_Number( g_FormationLeader, "TurnRadius" ); CalculateWPTurningPosition(speed, g_TotalTimeElapsed, g_TurnLeft, radius, g_PX, g_PZ, g_QX, g_QZ, g_CurveLen, g_StartAngle, g_FinalDir, curX, curZ, curDir); SetObjectPatrolData(kewlett, g_FormationLeader, g_PatrolPathName, g_PatrolPathIndex, curX, curZ, curDir, g_PX, g_PZ, g_QX, g_QZ, g_CurveLen, g_StartAngle, g_FinalDir, g_StraightLen, g_TotalTimeElapsed, g_TurnLeft ); } /***************************************************************************/ /**************************************************************************** * OnReachDeliveryDestination() * Called when ready to resume the delivery * DAVIDE July 5 2002 ****************************************************************************/ OnReachDeliveryDestination() { VAR len; // if the pathindex is the same as the length len = GetPatrolPathLength(g_PatrolPathName); if(g_PatrolPathIndex >= len) { SetDirection(THIS, g_LastSavedDir); SetAIState("Idle"); } else { SetDirection(THIS, g_LastSavedDir); SetAIState( "Receive Troops" ); } } /***************************************************************************/ /**************************************************************************** * ExplodeVehicle() * Since the transport is so long we use two explosions. We want it to look * good to the viewer so we use two explosions, the bigger explosion we always * orient in front of the viewer for more effect and to help mask the mesh * popping. We place a smaller one at the other end of the vehicle. * PeterM July 11 2002 ****************************************************************************/ ExplodeVehicle() { VAR explosion_angle, x, y, z, enemy; // Assume the current or closest enemy is the viewer if (g_CurrentEnemy == 0) { enemy = ClosestEnemy(); } else { enemy = g_CurrentEnemy; } // Get the angle to the enemy explosion_angle = GetAngleTo(THIS, enemy); // First explosion x = GetX(THIS) + 100 * Sin(explosion_angle); y = GetY(THIS); z = GetZ(THIS) + 100 * Cos(explosion_angle); FX_AddVehicleExplosion(x, y, z, 35, 10, 10); StopTurretVehicleSoundsUponDeath(); PlayTurretVehicleExplosionSound(); if ( g_Damaged0 != -1 ) FX_DeleteEffect( g_Damaged0 ); if ( g_Damaged1 != -1 ) FX_DeleteEffect( g_Damaged1 ); if ( g_Damaged2 != -1 ) FX_DeleteEffect( g_Damaged2 ); if ( g_Damaged3 != -1 ) FX_DeleteEffect( g_Damaged3 ); if ( g_Damaged4 != -1 ) FX_DeleteEffect( g_Damaged4 ); if ( g_Damaged5 != -1 ) FX_DeleteEffect( g_Damaged5 ); if ( g_Damaged6 != -1 ) FX_DeleteEffect( g_Damaged6 ); if ( g_Damaged7 != -1 ) FX_DeleteEffect( g_Damaged7 ); } /***************************************************************************/ /**************************************************************************** * PlayOpenLaunchDoorSound() * Plays the launch door open sound * PETERM July 21 2002 ****************************************************************************/ PlayOpenLaunchDoorSound() { // Check to see if we're audible, if not, then return if ( IsAudible(THIS, PARAM_TurretVehicle_RevDownSoundMaxDistance) == 0 ) { return; } // Get the turret rotation sound gt_Audio = FX_QueryFreeSoundFromPool(PARAM_TurretVehicle_RevDownSoundPool); if (gt_Audio != "") { FX_SetSoundDistances(gt_Audio, PARAM_TurretVehicle_RevDownSoundMinDistance, PARAM_TurretVehicle_RevDownSoundMaxDistance); FX_PlaySound( THIS, gt_Audio, 1.0, 1 ); } } /***************************************************************************/ /**************************************************************************** * CloseLaunchDoorSound() * Called when ready to resume the delivery * PETERM July 21 2002 ****************************************************************************/ PlayCloseLaunchDoorSound() { // Check to see if we're audible, if not, then return if ( IsAudible(THIS, PARAM_TurretVehicle_RevDownSoundMaxDistance) == 0 ) { return; } // Get the turret rotation sound gt_Audio = FX_QueryFreeSoundFromPool(PARAM_TurretVehicle_RevDownSoundPool); if (gt_Audio != "") { FX_SetSoundDistances(gt_Audio, PARAM_TurretVehicle_RevDownSoundMinDistance, PARAM_TurretVehicle_RevDownSoundMaxDistance); FX_PlaySound( THIS, gt_Audio, 1.0, 1 ); } } /***************************************************************************/ /**************************************************************************** * SetAIState() * Called to change the creature's AI State to a new state * PeterM June 19 2002 ****************************************************************************/ SetAIState( newState ) { // Call the base class method ::SetAIState( newState ); if ( newState == "Deploy Troops" ) { Begin_STATE_DeployTroops(); } if ( newState == "DefendPackage" ) { Begin_STATE_DefendPackage(); } if ( newState == "Receive Troops" ) { Begin_STATE_ReceiveTroops(); } } /***************************************************************************/ /**************************************************************************** * OnTick() * Called every game tick. Update AI states. * TimS June 19 2002 ****************************************************************************/ OnTick( seconds ) { VAR health, maxHealth, damage, level; //DEBUG("kewlett debug OnTick"); ::OnTick(seconds); health = QueryProperty_Number( THIS, "Health" ); maxHealth = QueryProperty_Number( THIS, "MaxHealth" ); damage = 1.0 - (health / maxHealth); if ( damage > g_DamageState ) { level = 0.25; if ( damage >= level && g_DamageState < level ) { g_Damaged0 = FX_AddShortCircuit( THIS, "NeoFXLib\\ElectricShort01.Neo", 0, 50, 125 ); //front g_Damaged1 = FX_AddShortCircuit( THIS, "NeoFXLib\\ElectricShort01.Neo", 50, 50, 65 ); //right01 } level = 0.5; if ( damage >= level && g_DamageState < level ) { g_Damaged2 = FX_AddShortCircuit( THIS, "NeoFXLib\\ElectricShort01.Neo", 50, 50, -20 ); //right02 g_Damaged3 = FX_AddShortCircuit( THIS, "NeoFXLib\\ElectricShort01.Neo", 40, 50, -100 ); //right03 } level = 0.75; if ( damage >= level && g_DamageState < level ) { g_Damaged4 = FX_AddShortCircuit( THIS, "NeoFXLib\\ElectricShort01.Neo", -50, 50, 65 ); //left01 g_Damaged5 = FX_AddShortCircuit( THIS, "NeoFXLib\\ElectricShort01.Neo", -50, 50, -20 ); //left02 } level = 0.9; if ( damage >= level && g_DamageState < level ) { g_Damaged6 = FX_AddShortCircuit( THIS, "NeoFXLib\\ElectricShort01.Neo", -40, 50, -100 ); //left03 g_Damaged7 = FX_AddShortCircuit( THIS, "NeoFXLib\\ElectricShort03.Neo", 0, 100, 40 ); //center } g_DamageState = damage; } /* if ( g_CurrentState == "Idle" ) { HeartBeat_STATE_Idle(); } */ if ( g_CurrentState == "Deploy Troops" ) { HeartBeat_STATE_DeployTroops( seconds ); } if ( g_CurrentState == "DefendPackage" ) { HeartBeat_STATE_DefendPackage( seconds ); } if ( g_CurrentState == "Receive Troops" ) { HeartBeat_STATE_ReceiveTroops( seconds ); } } /***************************************************************************/