// This script contains functionality to allow bots to watch for mountable vehicles (tanks) and then // mount them if(ScriptGoals.mount_vehicle == null) { ScriptGoals.mount_vehicle = table(); // The name for this goal ScriptGoals.mount_vehicle.Name = "GOAL_MOUNT_VEHICLE"; //////////////////////////////////////////////////////////////////////////////// // A debug flag for enabling/disabling printed messages. Useful for debugging this goal. ScriptGoals.mount_vehicle.Debug = false; //////////////////////////////////////////////////////////////////////////////// // Vehicle timeout in seconds ScriptGoals.mount_vehicle.Timeout = 30; //////////////////////////////////////////////////////////////////////////////// // Goal timeout in seconds ScriptGoals.mount_vehicle.GoalTimeout = 15; //////////////////////////////////////////////////////////////////////////////// // Number and maximum of escort bots ScriptGoals.mount_vehicle.MaxMounters = 3; //////////////////////////////////////////////////////////////////////////////// // Bot escort users table ScriptGoals.mount_vehicle.Users = table(); //////////////////////////////////////////////////////////////////////////////// // Script Goal Options // This script implements 2 aim modes while mounted. ScriptGoals.mount_vehicle.aimModes = { // vehicleVelocity makes the bot aim in the direction the vehicle is moving vehicleVelocity = function() { vehicleVelocity = GetEntVelocity(this.scriptgoaldata.vehicleEntity); if(vehicleVelocity && !vehicleVelocity.IsZero()) { vehicleVelocity.Normalize(); this.scriptgoaldata.aimDirection = vehicleVelocity; } }, // random360 will allow the bot to aim in random directions in a 360 degree arc, changing direction at defined intervals. // see ScriptGoals.mount_vehicle.aimAdjustDelay random360 = function() { // Is it time to adjust our aim? currentTime = GetTime(); if(this.scriptgoaldata.aimAdjust <= currentTime) { this.scriptgoaldata.aimDirection.RotateAroundZ(RandRange(0.0, 360.0)); // Schedule the next aim. this.scriptgoaldata.aimAdjust = currentTime + ScriptGoals.mount_vehicle.aimAdjustDelay; } }, }; //////////////////////////////////////////////////////////////////////////////// // Set which aim mode you want to use. ScriptGoals.mount_vehicle.useAimMode = ScriptGoals.mount_vehicle.aimModes.vehicleVelocity; //////////////////////////////////////////////////////////////////////////////// // The delay between aim adjustments. Only applies to random360 aim mode. ScriptGoals.mount_vehicle.aimAdjustDelay = 5000; //////////////////////////////////////////////////////////////////////////////// // The rate at which the pan360 goal will pan, in degrees ScriptGoals.mount_vehicle.aimPanRate = 1.0; //////////////////////////////////////////////////////////////////////////////// ScriptGoals.mount_vehicle.watchForMountableVehiclesFailed = function(bot) { bot.SetScriptControlled(false); bot.scriptgoal = null; bot.scriptgoaldata = null; if( ScriptGoals.mount_vehicle.Debug ) { print("ScriptGoals.mount_vehicle.watchForMountableVehicles Failed"); } }; //////////////////////////////////////////////////////////////////////////////// // Exit threads flag ScriptGoals.mount_vehicle.Exit = false; //////////////////////////////////////////////////////////////////////////////// ScriptGoals.mount_vehicle.watchForMountableVehiclesExit = function() { ScriptGoals.mount_vehicle.Exit = true; if( ScriptGoals.mount_vehicle.Debug ) { print("ScriptGoals.mount_vehicle.watchForMountableVehicles Exited"); } }; //////////////////////////////////////////////////////////////////////////////// ScriptGoals.mount_vehicle.watchForMountableVehiclesReset = function() { ScriptGoals.mount_vehicle.Exit = false; if( ScriptGoals.mount_vehicle.Debug ) { print("ScriptGoals.mount_vehicle.watchForMountableVehicles Reset"); } }; //////////////////////////////////////////////////////////////////////////////// ScriptGoals.mount_vehicle.watchForMountableVehicles = function() { if ( !Util.VehiclesInMap() || GetEntFlags(this.GetGameEntity(), ENTFLAG.LIMBO ) ) { exit(); } // Check if the bot already has this goal. if(this.WatchForMountableVehicleThread) { threadKill(this.WatchForMountableVehicleThread); } this.WatchForMountableVehicleThread = threadId(); if ( !TestMap && ( this.GetClass() == CLASS.MEDIC || this.GetClass() == CLASS.ENGINEER ) ) { exit(); } if ( ScriptGoals.mount_vehicle.Users[ this ] == null ) { if ( tableCount( ScriptGoals.mount_vehicle.Users ) + 1 > ScriptGoals.mount_vehicle.MaxMounters ) { exit(); } } if( ScriptGoals.mount_vehicle.Debug ) { print("ScriptGoals.mount_vehicle.watchForMountableVehicles"); } sleep( 2 ); botEnt = this.GetGameEntity(); framecounter = 0; mountcounter =0; while(true) { if ( ScriptGoals.mount_vehicle.Exit || GetEntFlags(this.GetGameEntity(), ENTFLAG.LIMBO ) ) { exit(); } // Do we have this goal already? if(this.scriptgoal == ScriptGoals.mount_vehicle.Name) { vehicleEntity = this.scriptgoaldata.vehicleEntity; if(GetEntFlags(vehicleEntity, ENTFLAG.DEAD)) { ScriptGoals.mount_vehicle.watchForMountableVehiclesFailed(this); yield(); continue; } if(!GetEntFlags(botEnt, ENTFLAG.MOUNTED)) { // If the tank is already mounted, someone beat us to it, so fail. if(GetEntFlags(vehicleEntity, ENTFLAG.MOUNTED)) { ScriptGoals.mount_vehicle.watchForMountableVehiclesFailed(this); if(ScriptGoals.mount_vehicle.Debug) { print("Someone beat me to it"); } } else { vehPos = GetEntPosition(vehicleEntity); this.GoTo( vehPos, Vector3( 0.0, 0.0, 0.0 ) ); if ( block(EVENT.GOAL_SUCCESS, EVENT.GOAL_FAILED) == EVENT.GOAL_FAILED ) { ScriptGoals.mount_vehicle.watchForMountableVehiclesFailed(this); yield(); continue; } while ( !GetEntFlags( botEnt, ENTFLAG.MOUNTED ) ) { vehPos = GetEntPosition(vehicleEntity); // If we lose sight of it, let's abandon the goal. // This should cover the case of when we die. if(!vehPos || !this.HasLineOfSightTo(vehPos, vehicleEntity)) { ScriptGoals.mount_vehicle.watchForMountableVehiclesFailed(this); break; } else { // Still available, so lets go for it. framecounter += 1; // Not mounted, lets try to get mounted. this.MoveTowards(vehPos); // A simple way to get the but to hit the use key and let off each frame. if(framecounter & 1) { this.PressButton(BTN.USE); } } if ( GetEntFlags( vehicleEntity, ENTFLAG.DEAD ) || ( !GetEntFlags( botEnt, ENTFLAG.MOUNTED ) && GetEntFlags( vehicleEntity, ENTFLAG.MOUNTED ) ) ) { ScriptGoals.mount_vehicle.watchForMountableVehiclesFailed( this ); break; } yield(); continue; } } } else { // Wohoo we're mounted. Let's do some simple logic for aiming the gun toward where the vehicle is going. // Allow the current aim mode to do any processing to change the aimDirection aimFunc = ScriptGoals.mount_vehicle.useAimMode; this:aimFunc(); // If the vehicle is moving & we have no target. let's face the direction of movement. if(!this.HasTarget()) { this.TurnToFacing(this.scriptgoaldata.aimDirection); } if ( !Util.IsVehicleAvailable( this, vehicleEntity ) ) { ScriptGoals.mount_vehicle.watchForMountableVehiclesFailed( this ); this.PressButton(BTN.USE); } if ( GetEntVelocity( vehicleEntity ) == Vector3( 0.0, 0.0, 0.0 ) ) { mountcounter = mountcounter + 1; if ( ( mountcounter / 20 ) == ScriptGoals.mount_vehicle.Timeout ) { ScriptGoals.mount_vehicle.watchForMountableVehiclesFailed( this ); this.PressButton(BTN.USE); mountcounter = 0; sleep( ScriptGoals.mount_vehicle.GoalTimeout ); } } else { mountcounter = 0; } } yield(); continue; } // Look for a vehicle to mount. //~ vehicle = this.GetNearest(CAT.MOVER, CLASS.VEHICLE_HVY); vehicle = Util.GetNearestVehicle( this ); if(vehicle) { // Is it mountable AND not already mounted? if ( Util.IsVehicleAvailable( this, vehicle ) && !GetEntFlags(vehicle, ENTFLAG.DEAD) && GetEntFlags( vehicle, ENTFLAG.MOUNTABLE ) && !GetEntFlags( vehicle, ENTFLAG.MOUNTED ) ) { if(ScriptGoals.mount_vehicle.Debug) { print("Mountable and not mounted"); } this.SetScriptControlled(true); // Set our script goal to the name of this goal. This name will show in the first person HUD for the bot. this.scriptgoal = ScriptGoals.mount_vehicle.Name; // make a table to hold extra goal data in. aimStart = GetEntFacing(vehicle); aimAdjustTime = GetTime() + ScriptGoals.mount_vehicle.aimAdjustDelay; this.scriptgoaldata = { aimDirection = aimStart, aimAdjust = aimAdjustTime, vehicleEntity = vehicle }; } else { if(ScriptGoals.mount_vehicle.Debug) { print("Not Mountable or already mounted!"); } } } sleep(1); } }; } // Add the following snippet somewhere, such as a spawn callback to use this goal // Make sure we don't have the thread already. //if(this.WatchForVehicleThread == null) //{ // this.WatchForVehicleThread = this:thread(ScriptGoals.mount_vehicle.watchForMountableVehicles); //}