internal void OnDetach(string[] args)
        {
            if (args?[0] == "help")
            {
                ShowTowControls();
                return;
            }
            else if (args?[0] == "cancel")
            {
                if (_attachmentStage != AttachmentStage.None)
                {
                    _attachmentStage = AttachmentStage.Cancel;

                    Game.PlaySound("CANCEL", "HUD_FREEMODE_SOUNDSET");
                }
                else
                {
                    Screen.ShowNotification("~r~You are not interacting with a vehicle right now!");
                }

                return;
            }

            OnRemoveLastAttachment();
        }
Example #2
0
		public void Restart()
		{
			Stage = AttachmentStage.Pending;
			CurrentChunk = 0;
			UploadedBytes = 0;
			DeckCloudFileResponse = null;
			DeckVersionResponse = null;
			Status = AttachmentStatus.Pending;
			DownloadLink = null;
		}
        internal void OnNewAttachment()
        {
            if (_attachmentStage != AttachmentStage.None)
            {
                Screen.ShowNotification("~r~You are already interacting with another vehicle!");
                return;
            }

            _attachmentStage = AttachmentStage.TowTruck;

            Game.PlaySound("TOGGLE_ON", "HUD_FRONTEND_DEFAULT_SOUNDSET");
            Screen.ShowNotification("~g~Select a towing vehicle to get started!");
        }
        internal void OnRemoveLastAttachment()
        {
            if (_attachments.Count == 0)
            {
                Screen.ShowNotification("~r~You do not have any vehicle to detach!");
                return;
            }

            if (_attachmentStage != AttachmentStage.None)
            {
                Screen.ShowNotification("~r~You are already interacting with another vehicle!");
                return;
            }

            _attachmentStage = AttachmentStage.Predetach;

            Game.PlaySound("TOGGLE_ON", "HUD_FRONTEND_DEFAULT_SOUNDSET");
            Screen.ShowNotification("~g~Follow the instructions to detach the vehicle.");
        }
        internal void OnControl(AttachmentControl attachmentControl)
        {
            if (_attachmentStage != AttachmentStage.Position && _attachmentStage != AttachmentStage.Detach)
            {
                return;
            }

            float changeAmount = CONFIG.ChangeAmount;

            if (_goFaster)
            {
                changeAmount += CONFIG.FasterAmount;
            }
            else if (_goSlower)
            {
                changeAmount += CONFIG.SlowerAmount;
            }

            Vector3
                position = _attachments.Last().AttachmentPosition,
                rotation = _attachments.Last().AttachmentRotation;

            Vehicle
                towVehicle        = (Vehicle)Entity.FromNetworkId(_attachments.Last().TowVehicle),
                vehicleBeingTowed = (Vehicle)Entity.FromNetworkId(_attachments.Last().VehicleBeingTowed);

            if (!Entity.Exists(towVehicle) || !Entity.Exists(vehicleBeingTowed))
            {
                Game.PlaySound("CANCEL", "HUD_FREEMODE_SOUNDSET");
                Screen.ShowNotification("~g~Attachment canceled.");

                _attachmentStage = AttachmentStage.Cancel;

                return;
            }

            switch (attachmentControl)
            {
            case AttachmentControl.Forward:
                position.Y += changeAmount;
                break;

            case AttachmentControl.Back:
                position.Y -= changeAmount;
                break;

            case AttachmentControl.Left:
                position.X -= changeAmount;
                break;

            case AttachmentControl.Right:
                position.X += changeAmount;
                break;

            case AttachmentControl.Up:
                position.Z += changeAmount;
                break;

            case AttachmentControl.Down:
                position.Z -= changeAmount;
                break;

            case AttachmentControl.RotateLeft:
                rotation.Z += changeAmount * 10;
                break;

            case AttachmentControl.RotateRight:
                rotation.Z -= changeAmount * 10;
                break;

            case AttachmentControl.RotateUp:
                rotation.X += changeAmount * 10;
                break;

            case AttachmentControl.RotateDown:
                rotation.X -= changeAmount * 10;
                break;

            case AttachmentControl.Confirm:
                TriggerServerEvent("Inferno-Collection:Vehicle-Attachment:RemoveInUseVehicle", _attachments.Last().TowVehicle);
                TriggerServerEvent("Inferno-Collection:Vehicle-Attachment:RemoveInUseVehicle", _attachments.Last().VehicleBeingTowed);

                if (_attachmentStage == AttachmentStage.Position)
                {
                    Screen.ShowNotification("~g~Attachment complete! Drive safe.");

                    vehicleBeingTowed.ResetOpacity();
                    vehicleBeingTowed.IsCollisionEnabled = true;
                }
                else if (_attachmentStage == AttachmentStage.Detach)
                {
                    Screen.ShowNotification($"~g~{vehicleBeingTowed.LocalizedName} detached!");

                    ResetTowedVehicle(vehicleBeingTowed);

                    _attachments.RemoveAll(
                        i =>
                        i.TowVehicle == _attachments.Last().TowVehicle&&
                        i.VehicleBeingTowed == _attachments.Last().VehicleBeingTowed
                        );
                }

                Game.PlaySound("WAYPOINT_SET", "HUD_FRONTEND_DEFAULT_SOUNDSET");

                _attachmentStage = AttachmentStage.None;

                return;
            }

            if (towVehicle.Position.DistanceToSquared(towVehicle.GetOffsetPosition(position)) > CONFIG.MaxDistanceFromTowVehicle)
            {
                Screen.ShowNotification("~r~Cannot move there, too far from tow vehicle!", true);
                return;
            }

            vehicleBeingTowed.AttachTo(towVehicle, position, rotation);

            // Store current position so we can reference it later
            _attachments.Last().AttachmentPosition = position;
            _attachments.Last().AttachmentRotation = rotation;
        }
        internal async Task OnTick()
        {
            switch (_attachmentStage)
            {
                #region No attachment
            case AttachmentStage.None:
                await Delay(1000);

                break;
                #endregion

                #region Selecting tow truck
            case AttachmentStage.TowTruck:
                {
                    Vehicle towTruck = FindVehicle();

                    if (towTruck == null)
                    {
                        Screen.DisplayHelpTextThisFrame("No vehicle found!");
                        return;
                    }

                    if (_vehiclesInUse.Contains(towTruck.NetworkId))
                    {
                        Screen.DisplayHelpTextThisFrame($"Someone else is using the {towTruck.LocalizedName} right now.");
                        return;
                    }

                    if (
                        (!CONFIG.BlacklistToWhitelist && CONFIG.AttachmentBlacklist.Contains(towTruck.Model)) ||
                        (CONFIG.BlacklistToWhitelist && !CONFIG.AttachmentBlacklist.Contains(towTruck.Model))
                        )
                    {
                        Screen.DisplayHelpTextThisFrame($"The {towTruck.LocalizedName} cannot be used as a tow vehicle!");
                        return;
                    }

                    if (CONFIG.EnableLine)
                    {
                        World.DrawLine(
                            Game.PlayerPed.Position, towTruck.Position,
                            System.Drawing.Color.FromArgb(255, 0, 255, 0)
                            );
                    }

                    Screen.DisplayHelpTextThisFrame($"Press ~INPUT_FRONTEND_ACCEPT~ to use the {towTruck.LocalizedName} as the towing vehicle.");

                    if (!Game.IsControlJustPressed(0, Control.FrontendAccept))
                    {
                        return;
                    }

                    Game.PlaySound("OK", "HUD_FRONTEND_DEFAULT_SOUNDSET");
                    Screen.ShowNotification($"~g~{towTruck.LocalizedName} confirmed as towing vehicle! Now select a vehicle to be towed.");

                    _attachmentStage = AttachmentStage.VehicleToBeTowed;
                    _attachments.Add(new Towing()
                    {
                        TowVehicle = towTruck.NetworkId
                    });

                    TriggerServerEvent("Inferno-Collection:Vehicle-Attachment:AddInUseVehicle", towTruck.NetworkId);

                    await Delay(1000);
                }
                break;
                #endregion

                #region Selecting vehicle to be towed
            case AttachmentStage.VehicleToBeTowed:
                {
                    Vehicle
                        vehicleToBeTowed = FindVehicle(),
                        towVehicle       = (Vehicle)Entity.FromNetworkId(_attachments.Last().TowVehicle);

                    if (vehicleToBeTowed == null)
                    {
                        Screen.DisplayHelpTextThisFrame("No vehicle that be can towed found!");
                        return;
                    }

                    if (
                        _vehiclesInUse.Contains(vehicleToBeTowed.NetworkId) ||
                        // If vehicle is already attached to another vehicle
                        _attachments.Any(i => i.VehicleBeingTowed == vehicleToBeTowed.NetworkId)
                        )
                    {
                        Screen.DisplayHelpTextThisFrame($"The {vehicleToBeTowed.LocalizedName} is already in use.");
                        break;
                    }

                    if (
                        (!CONFIG.BlacklistToWhitelist && CONFIG.AttachmentBlacklist.Contains(vehicleToBeTowed.Model))
                        ||
                        (
                            CONFIG.BlacklistToWhitelist && CONFIG.WhitelistForTowedVehicles &&
                            !CONFIG.AttachmentBlacklist.Contains(vehicleToBeTowed.Model)
                        )
                        )
                    {
                        Screen.DisplayHelpTextThisFrame($"The {vehicleToBeTowed.LocalizedName} cannot be towed!");
                        return;
                    }

                    if (vehicleToBeTowed.Occupants.Length > 0)
                    {
                        Screen.DisplayHelpTextThisFrame($"The {vehicleToBeTowed.LocalizedName} is occupied!");
                        return;
                    }

                    if (
                        Entity.Exists(towVehicle) &&
                        vehicleToBeTowed.Position.DistanceToSquared2D(towVehicle.Position) > CONFIG.MaxDistanceFromTowVehicle
                        )
                    {
                        Screen.DisplayHelpTextThisFrame($"The {vehicleToBeTowed.LocalizedName} is too far from the {towVehicle.LocalizedName}!");
                        return;
                    }

                    if (CONFIG.EnableLine)
                    {
                        World.DrawLine(
                            Game.PlayerPed.Position, vehicleToBeTowed.Position,
                            System.Drawing.Color.FromArgb(255, 0, 255, 0)
                            );
                    }

                    Screen.DisplayHelpTextThisFrame($"Press ~INPUT_FRONTEND_ACCEPT~ to tow the {vehicleToBeTowed.LocalizedName}.");

                    if (!Game.IsControlJustPressed(0, Control.FrontendAccept))
                    {
                        return;
                    }

                    if (!Entity.Exists(towVehicle))
                    {
                        _attachmentStage = AttachmentStage.Cancel;

                        Game.PlaySound("CANCEL", "HUD_FREEMODE_SOUNDSET");
                        Screen.ShowNotification("~r~Tow vehicle deleted, cannot attach to nothing!");
                        break;
                    }

                    _attachmentStage = AttachmentStage.Position;
                    _attachments.Last().VehicleBeingTowed = vehicleToBeTowed.NetworkId;

                    TriggerServerEvent("Inferno-Collection:Vehicle-Attachment:AddInUseVehicle", vehicleToBeTowed.NetworkId);

                    int timeout = 4;
                    API.NetworkRequestControlOfNetworkId(vehicleToBeTowed.NetworkId);
                    while (
                        timeout > 0 &&
                        !API.NetworkHasControlOfNetworkId(vehicleToBeTowed.NetworkId)
                        )
                    {
                        await Delay(250);

                        API.NetworkRequestControlOfNetworkId(vehicleToBeTowed.NetworkId);

                        timeout--;
                    }

                    if (!API.NetworkHasControlOfNetworkId(vehicleToBeTowed.NetworkId))
                    {
                        Game.PlaySound("CANCEL", "HUD_FREEMODE_SOUNDSET");
                        Screen.ShowNotification($"~r~Could not tow the {vehicleToBeTowed.LocalizedName}.", true);

                        Debug.WriteLine($"Unable to tow {vehicleToBeTowed.LocalizedName} ({vehicleToBeTowed.NetworkId}); ownership of the vehicle could not be requested!");

                        _attachmentStage = AttachmentStage.Cancel;

                        break;
                    }

                    Game.PlaySound("OK", "HUD_FRONTEND_DEFAULT_SOUNDSET");
                    Screen.ShowNotification($"~g~{vehicleToBeTowed.LocalizedName} confirmed as vehicle to be towed! Follow instructions to position vehicle.");

                    ShowTowControls();

                    vehicleToBeTowed.Opacity            = 225;
                    vehicleToBeTowed.IsPersistent       = true;
                    vehicleToBeTowed.IsPositionFrozen   = true;
                    vehicleToBeTowed.IsCollisionEnabled = false;
                    vehicleToBeTowed.LockStatus         = VehicleLockStatus.CannotBeTriedToEnter;
                    vehicleToBeTowed.AttachTo(towVehicle, POSITION_VECTOR, ROTATION_VECTOR);

                    _attachments.Last().AttachmentPosition = POSITION_VECTOR;
                    _attachments.Last().AttachmentRotation = ROTATION_VECTOR;

                    await Delay(1000);
                }
                break;
                #endregion

                #region Remove attached vehicle
            case AttachmentStage.Predetach:
                {
                    ShowTowControls();

                    Entity vehicleBeingTowed = Entity.FromNetworkId(_attachments.Last().VehicleBeingTowed);

                    if (!Entity.Exists(vehicleBeingTowed))
                    {
                        _attachmentStage = AttachmentStage.Cancel;

                        Game.PlaySound("CANCEL", "HUD_FREEMODE_SOUNDSET");
                        Screen.ShowNotification("~r~Vehicle being towed deleted!");
                        break;
                    }

                    vehicleBeingTowed.Opacity = 225;
                    _attachmentStage          = AttachmentStage.Detach;
                }
                break;
                #endregion

                #region Cancel current attachments
            case AttachmentStage.Cancel:
            {
                if (_attachments.Count > 0)
                {
                    Entity vehicleBeingTowed = Entity.FromNetworkId(_attachments.Last().VehicleBeingTowed);

                    if (Entity.Exists(vehicleBeingTowed))
                    {
                        ResetTowedVehicle(vehicleBeingTowed);
                    }

                    // Even though the vehicles may no longer exist, we still need to clear the Network IDs in case they get reused
                    TriggerServerEvent("Inferno-Collection:Vehicle-Attachment:RemoveInUseVehicle", _attachments.Last().TowVehicle);
                    TriggerServerEvent("Inferno-Collection:Vehicle-Attachment:RemoveInUseVehicle", _attachments.Last().VehicleBeingTowed);

                    _attachments.RemoveAll(
                        i =>
                        i.TowVehicle == _attachments.Last().TowVehicle&&
                        i.VehicleBeingTowed == _attachments.Last().VehicleBeingTowed
                        );
                }

                _attachmentStage = AttachmentStage.None;
            }
            break;
                #endregion

            default:
                if (Game.IsControlPressed(0, Control.Sprint))
                {
                    _goFaster = true;
                    _goSlower = false;
                    break;
                }
                else if (Game.IsControlPressed(0, Control.Duck))
                {
                    _goFaster = false;
                    _goSlower = true;
                    break;
                }

                _goFaster = false;
                _goSlower = false;
                break;
            }
        }
Example #7
0
        internal async Task OnTick()
        {
            switch (_attachmentStage)
            {
                #region No attachment
            case AttachmentStage.None:
                await Delay(1000);

                break;
                #endregion

                #region Selecting tow truck
            case AttachmentStage.TowTruck:
                {
                    Vehicle towTruck = FindVehicle();

                    if (towTruck == null)
                    {
                        Screen.DisplayHelpTextThisFrame("No vehicle found!");
                        return;
                    }

                    if (_vehiclesInUse.Contains(towTruck.NetworkId))
                    {
                        Screen.DisplayHelpTextThisFrame($"Someone else is using the {towTruck.LocalizedName} right now.");
                        return;
                    }

                    if (
                        (!CONFIG.BlacklistToWhitelist && CONFIG.AttachmentBlacklist.Contains(towTruck.Model)) ||
                        (CONFIG.BlacklistToWhitelist && !CONFIG.AttachmentBlacklist.Contains(towTruck.Model))
                        )
                    {
                        Screen.DisplayHelpTextThisFrame($"The {towTruck.LocalizedName} cannot be used as a tow vehicle!");
                        return;
                    }

                    if (CONFIG.EnableLine)
                    {
                        World.DrawLine(
                            Game.PlayerPed.Position, towTruck.Position,
                            System.Drawing.Color.FromArgb(255, 0, 255, 0)
                            );
                    }

                    Screen.DisplayHelpTextThisFrame($"Press ~INPUT_FRONTEND_ACCEPT~ to use the {towTruck.LocalizedName} as the towing vehicle.");

                    if (!Game.IsControlJustPressed(0, Control.FrontendAccept))
                    {
                        return;
                    }

                    Game.PlaySound("OK", "HUD_FRONTEND_DEFAULT_SOUNDSET");
                    Screen.ShowNotification($"~g~{towTruck.LocalizedName} confirmed as towing vehicle! Now select a vehicle to be towed.");

                    _attachmentStage = AttachmentStage.VehicleToBeTowed;
                    _attachments.Add(new Towing()
                    {
                        TowVehicle = towTruck.NetworkId
                    });

                    TriggerServerEvent("Inferno-Collection:Vehicle-Attachment:AddInUseVehicle", towTruck.NetworkId);

                    await Delay(1000);
                }
                break;
                #endregion

                #region Selecting vehicle to be towed
            case AttachmentStage.VehicleToBeTowed:
                {
                    Vehicle
                        vehicleToBeTowed = FindVehicle(),
                        towVehicle       = (Vehicle)Entity.FromNetworkId(_attachments.Last().TowVehicle);

                    if (vehicleToBeTowed == null)
                    {
                        Screen.DisplayHelpTextThisFrame("No vehicle that be can towed found!");
                        return;
                    }

                    if (
                        _vehiclesInUse.Contains(vehicleToBeTowed.NetworkId) ||
                        // If vehicle is already attached to another vehicle
                        _attachments.Any(i => i.VehicleBeingTowed == vehicleToBeTowed.NetworkId)
                        )
                    {
                        Screen.DisplayHelpTextThisFrame($"The {vehicleToBeTowed.LocalizedName} is already in use.");
                        break;
                    }

                    if (
                        (!CONFIG.BlacklistToWhitelist && CONFIG.AttachmentBlacklist.Contains(vehicleToBeTowed.Model))
                        ||
                        (
                            CONFIG.BlacklistToWhitelist && CONFIG.WhitelistForTowedVehicles &&
                            !CONFIG.AttachmentBlacklist.Contains(vehicleToBeTowed.Model)
                        )
                        )
                    {
                        Screen.DisplayHelpTextThisFrame($"The {vehicleToBeTowed.LocalizedName} cannot be towed!");
                        return;
                    }

                    if (vehicleToBeTowed.Occupants.Length > 0)
                    {
                        Screen.DisplayHelpTextThisFrame($"The {vehicleToBeTowed.LocalizedName} is occupied!");
                        return;
                    }

                    if (
                        Entity.Exists(towVehicle) &&
                        vehicleToBeTowed.Position.DistanceToSquared2D(towVehicle.Position) > CONFIG.MaxDistanceFromTowVehicle
                        )
                    {
                        Screen.DisplayHelpTextThisFrame($"The {vehicleToBeTowed.LocalizedName} is too far from the {towVehicle.LocalizedName}!");
                        return;
                    }

                    if (CONFIG.EnableLine)
                    {
                        World.DrawLine(
                            Game.PlayerPed.Position, vehicleToBeTowed.Position,
                            System.Drawing.Color.FromArgb(255, 0, 255, 0)
                            );
                    }

                    Screen.DisplayHelpTextThisFrame($"Press ~INPUT_FRONTEND_ACCEPT~ to tow the {vehicleToBeTowed.LocalizedName}.");

                    if (!Game.IsControlJustPressed(0, Control.FrontendAccept))
                    {
                        return;
                    }

                    if (!Entity.Exists(towVehicle))
                    {
                        _attachmentStage = AttachmentStage.Cancel;

                        Game.PlaySound("CANCEL", "HUD_FREEMODE_SOUNDSET");
                        Screen.ShowNotification("~r~Tow vehicle deleted, cannot attach to nothing!");
                        break;
                    }

                    _attachmentStage = AttachmentStage.Position;
                    _attachments.Last().VehicleBeingTowed = vehicleToBeTowed.NetworkId;

                    TriggerServerEvent("Inferno-Collection:Vehicle-Attachment:AddInUseVehicle", vehicleToBeTowed.NetworkId);

                    int timeout = 4;
                    API.NetworkRequestControlOfNetworkId(vehicleToBeTowed.NetworkId);
                    while (
                        timeout > 0 &&
                        !API.NetworkHasControlOfNetworkId(vehicleToBeTowed.NetworkId)
                        )
                    {
                        await Delay(250);

                        API.NetworkRequestControlOfNetworkId(vehicleToBeTowed.NetworkId);

                        timeout--;
                    }

                    if (!API.NetworkHasControlOfNetworkId(vehicleToBeTowed.NetworkId))
                    {
                        Game.PlaySound("CANCEL", "HUD_FREEMODE_SOUNDSET");
                        Screen.ShowNotification($"~r~Could not tow the {vehicleToBeTowed.LocalizedName}.", true);

                        Debug.WriteLine($"Unable to tow {vehicleToBeTowed.LocalizedName} ({vehicleToBeTowed.NetworkId}); ownership of the vehicle could not be requested!");

                        _attachmentStage = AttachmentStage.Cancel;

                        break;
                    }

                    Game.PlaySound("OK", "HUD_FRONTEND_DEFAULT_SOUNDSET");
                    Screen.ShowNotification($"~g~{vehicleToBeTowed.LocalizedName} confirmed as vehicle to be towed! Follow instructions to position vehicle.");

                    ShowTowControls();

                    vehicleToBeTowed.Opacity            = 225;
                    vehicleToBeTowed.IsPersistent       = true;
                    vehicleToBeTowed.IsPositionFrozen   = true;
                    vehicleToBeTowed.IsCollisionEnabled = false;
                    vehicleToBeTowed.LockStatus         = VehicleLockStatus.CannotBeTriedToEnter;
                    vehicleToBeTowed.AttachTo(towVehicle, POSITION_VECTOR, ROTATION_VECTOR);

                    _attachments.Last().AttachmentPosition = POSITION_VECTOR;
                    _attachments.Last().AttachmentRotation = ROTATION_VECTOR;

                    await Delay(1000);
                }
                break;
                #endregion

                #region Remove attached vehicle
            case AttachmentStage.Predetach:
                {
                    ShowTowControls();

                    Entity vehicleBeingTowed = Entity.FromNetworkId(_attachments.Last().VehicleBeingTowed);

                    if (!Entity.Exists(vehicleBeingTowed))
                    {
                        _attachmentStage = AttachmentStage.Cancel;

                        Game.PlaySound("CANCEL", "HUD_FREEMODE_SOUNDSET");
                        Screen.ShowNotification("~r~Vehicle being towed deleted!");
                        break;
                    }

                    vehicleBeingTowed.Opacity = 225;
                    _attachmentStage          = AttachmentStage.Detach;
                }
                break;
                #endregion

                #region Cancel current attachments
            case AttachmentStage.Cancel:
            {
                if (_attachments.Count > 0)
                {
                    Entity vehicleBeingTowed = Entity.FromNetworkId(_attachments.Last().VehicleBeingTowed);

                    if (Entity.Exists(vehicleBeingTowed))
                    {
                        ResetTowedVehicle(vehicleBeingTowed);
                    }

                    // Even though the vehicles may no longer exist, we still need to clear the Network IDs in case they get reused
                    TriggerServerEvent("Inferno-Collection:Vehicle-Attachment:RemoveInUseVehicle", _attachments.Last().TowVehicle);
                    TriggerServerEvent("Inferno-Collection:Vehicle-Attachment:RemoveInUseVehicle", _attachments.Last().VehicleBeingTowed);

                    _attachments.RemoveAll(
                        i =>
                        i.TowVehicle == _attachments.Last().TowVehicle&&
                        i.VehicleBeingTowed == _attachments.Last().VehicleBeingTowed
                        );
                }

                _attachmentStage = AttachmentStage.None;
            }
            break;
                #endregion

                #region Adjust vehicle position
            default:
            {
                float changeAmount = CONFIG.ChangeAmount;

                Vector3
                    position = _attachments.Last().AttachmentPosition,
                    rotation = _attachments.Last().AttachmentRotation;

                bool
                    fast = Game.IsControlPressed(0, Control.Sprint),
                    slow = Game.IsControlPressed(0, Control.VehicleSubDescend);

                Vehicle
                    towVehicle        = (Vehicle)Entity.FromNetworkId(_attachments.Last().TowVehicle),
                    vehicleBeingTowed = (Vehicle)Entity.FromNetworkId(_attachments.Last().VehicleBeingTowed);

                // Because FiveM only takes one input at a time, this is how we
                // check if Shift or Ctrl are being held as well as another key
                if (fast)
                {
                    _goFaster = CrossFrameControl.True;
                }
                else if (!fast && _goFaster == CrossFrameControl.True)
                {
                    _goFaster = CrossFrameControl.FalseNextFrame;
                }
                else if (_goFaster == CrossFrameControl.FalseNextFrame)
                {
                    _goFaster = CrossFrameControl.False;
                }

                if (!fast && slow)
                {
                    _goSlower = CrossFrameControl.True;
                }
                else if (!slow && _goSlower == CrossFrameControl.True)
                {
                    _goSlower = CrossFrameControl.FalseNextFrame;
                }
                else if (_goSlower == CrossFrameControl.FalseNextFrame)
                {
                    _goSlower = CrossFrameControl.False;
                }

                if (_goFaster != CrossFrameControl.False)
                {
                    changeAmount += CONFIG.FasterAmount;
                }

                if (_goSlower != CrossFrameControl.False)
                {
                    changeAmount += CONFIG.SlowerAmount;
                }

                // Gets new position based of old position +/- increase amount
                // NUMPAD 8
                if (Game.IsControlJustPressed(0, Control.VehicleFlyPitchUpOnly))
                {
                    position.Y += changeAmount;
                }
                // NUMPAD 5
                else if (Game.IsControlJustPressed(0, Control.VehicleFlyPitchDownOnly))
                {
                    position.Y -= changeAmount;
                }
                // NUMPAD 4
                else if (Game.IsControlJustPressed(0, Control.VehicleFlyRollLeftOnly))
                {
                    position.X -= changeAmount;
                }
                // NUMPAD 6
                else if (Game.IsControlJustPressed(0, Control.VehicleFlyRollRightOnly))
                {
                    position.X += changeAmount;
                }
                // NUMPAD +
                else if (Game.IsControlJustPressed(0, Control.ReplayFOVIncrease))
                {
                    position.Z += changeAmount;
                }
                // NUMPAD -
                else if (Game.IsControlJustPressed(0, Control.ReplayFOVDecrease))
                {
                    position.Z -= changeAmount;
                }

                // Gets new rotation based of old position +/- increase amount
                // NUMPAD 7
                else if (Game.IsControlJustPressed(0, Control.VehicleFlySelectTargetLeft))
                {
                    rotation.Z += changeAmount * 10;
                }
                // NUMPAD 9
                else if (Game.IsControlJustPressed(0, Control.VehicleFlySelectTargetRight))
                {
                    rotation.Z -= changeAmount * 10;
                }

                if (!Entity.Exists(towVehicle) || !Entity.Exists(vehicleBeingTowed))
                {
                    Game.PlaySound("CANCEL", "HUD_FREEMODE_SOUNDSET");
                    Screen.ShowNotification("~g~Attachment canceled.");

                    _attachmentStage = AttachmentStage.Cancel;

                    return;
                }

                // Confirms placement
                // NUMPAD Enter
                else if (Game.IsControlJustPressed(0, Control.FrontendAccept))
                {
                    TriggerServerEvent("Inferno-Collection:Vehicle-Attachment:RemoveInUseVehicle", _attachments.Last().TowVehicle);
                    TriggerServerEvent("Inferno-Collection:Vehicle-Attachment:RemoveInUseVehicle", _attachments.Last().VehicleBeingTowed);

                    if (_attachmentStage == AttachmentStage.Position)
                    {
                        Screen.ShowNotification("~g~Attachment complete! Drive safe.");

                        vehicleBeingTowed.ResetOpacity();
                        vehicleBeingTowed.IsCollisionEnabled = true;
                    }
                    else if (_attachmentStage == AttachmentStage.Detach)
                    {
                        Screen.ShowNotification($"~g~{vehicleBeingTowed.LocalizedName} deatached!");

                        ResetTowedVehicle(vehicleBeingTowed);

                        _attachments.RemoveAll(
                            i =>
                            i.TowVehicle == _attachments.Last().TowVehicle&&
                            i.VehicleBeingTowed == _attachments.Last().VehicleBeingTowed
                            );
                    }

                    Game.PlaySound("WAYPOINT_SET", "HUD_FRONTEND_DEFAULT_SOUNDSET");

                    _attachmentStage = AttachmentStage.None;

                    return;
                }

                // Checks for new position or rotation, so attching is not done every frame
                if (position != _attachments.Last().AttachmentPosition || rotation != _attachments.Last().AttachmentRotation)
                {
                    if (towVehicle.Position.DistanceToSquared2D(
                            towVehicle.GetOffsetPosition(position)
                            ) > CONFIG.MaxDistanceFromTowVehicle)
                    {
                        Screen.ShowNotification("~r~Cannot move there, too far from tow vehicle!", true);
                        return;
                    }

                    vehicleBeingTowed.AttachTo(towVehicle, position, rotation);

                    // Store current position so we can reference it later
                    _attachments.Last().AttachmentPosition = position;
                    _attachments.Last().AttachmentRotation = rotation;
                }
            }
            break;
                #endregion
            }
        }
		private void DoTask(ISkyDoxUploadTask task, UploadInfo uploadInfo, AttachmentStage nextStage, CancellationToken token, bool writeToFile)
		{
			if (!_shouldWork)
			{
				return;
			}

			if (task != null)
			{
				try
				{
					token.ThrowIfCancellationRequested();
					task.Run();
				}
				catch (OperationCanceledException)
				{
					_shouldWork = false;
					uploadInfo.Status = AttachmentStatus.Pending;
					throw;
				}
				catch (Exception)
				{
					_shouldWork = false;
					uploadInfo.Status = AttachmentStatus.Failed;
					throw;
				}

				ErrorMessage = task.ErrorMessage;
				if (!task.Succeeded)
				{
					Succeeded = false;
					_shouldWork = false;
					ShouldRetry = task.ShouldRetry;
					return;
				}
			}
			uploadInfo.Stage = nextStage;
			if (task != null && writeToFile)
			{
				_progressReporter.WriteToFile();
			}
		}