public static void ServerDespawnDrone(IDynamicWorldObject objectDrone, bool isReturnedToPlayer)
        {
            var privateState = objectDrone.GetPrivateState <DronePrivateState>();
            var publicState  = objectDrone.GetPublicState <DronePublicState>();

            publicState.ResetTargetPosition();

            if (privateState.IsDespawned)
            {
                return;
            }

            var droneItem      = privateState.AssociatedItem;
            var protoItemDrone = (IProtoItemDrone)droneItem.ProtoItem;
            var characterOwner = privateState.CharacterOwner;
            var world          = Server.World;

            var protoDrone = protoItemDrone.ProtoDrone;

            protoDrone.ServerOnDroneDroppedOrReturned(objectDrone, characterOwner, isReturnedToPlayer);

            // recreate physics (as despawned drone doesn't have any physics)
            privateState.IsDespawned = true;
            world.StopPhysicsBody(objectDrone.PhysicsBody);
            objectDrone.ProtoWorldObject.SharedCreatePhysics(objectDrone);
            world.SetPosition(objectDrone,
                              ServerCharacterDeathMechanic.ServerGetGraveyardPosition().ToVector2D());

            privateState.CharacterOwner = null;
            ServerOnDroneControlRemoved(characterOwner, objectDrone);

            var currentDurability = (int)(objectDrone.GetPublicState <DronePublicState>().StructurePointsCurrent
                                          / protoItemDrone.DurabilityToStructurePointsConversionCoefficient);

            if (currentDurability <= 1)
            {
                currentDurability = 0;
            }

            var deltaDurabilility = (int)(ItemDurabilitySystem.SharedGetDurabilityValue(droneItem)
                                          - currentDurability);

            if (deltaDurabilility <= 0)
            {
                return;
            }

            ItemDurabilitySystem.ServerModifyDurability(droneItem,
                                                        -deltaDurabilility);

            if (droneItem.IsDestroyed)
            {
                // drone item degraded to 100%, notify the player
                ItemDurabilitySystem.Instance.CallClient(characterOwner,
                                                         _ => _.ClientRemote_ItemBroke(droneItem.ProtoItem));
            }
        }
        public static IItem ClientSelectNextDrone(List <IItem> exceptItems)
        {
            IItem selectedItem      = null;
            var   selectedItemOrder = int.MinValue;
            var   privateState      = ClientCurrentCharacterHelper.PrivateState;

            // find a drone item with the highest order number
            foreach (var item in privateState.ContainerHotbar.Items)
            {
                if (IsValidDroneItem(item, out var order) &&
                    order >= selectedItemOrder)
                {
                    selectedItem      = item;
                    selectedItemOrder = order;
                }
            }

            foreach (var item in privateState.ContainerInventory.Items)
            {
                if (IsValidDroneItem(item, out var order) &&
                    order >= selectedItemOrder)
                {
                    selectedItem      = item;
                    selectedItemOrder = order;
                }
            }

            return(selectedItem);

            bool IsValidDroneItem(IItem item, out int order)
            {
                if (item.ProtoItem is IProtoItemDrone protoItemDrone &&
                    ItemDurabilitySystem.SharedGetDurabilityValue(item) > 0 &&
                    !exceptItems.Contains(item))
                {
                    order = protoItemDrone.SelectionOrder;
                    return(true);
                }

                order = int.MinValue;
                return(false);
            }
        }
        public void ServerRemote_StartDrone(IItem itemDrone, Vector2Ushort worldPosition)
        {
            var character = ServerRemoteContext.Character;

            if (itemDrone.Container.Owner != character)
            {
                Logger.Info("Player don't own the drone: " + itemDrone, character);
                return;
            }

            if (!(itemDrone.ProtoItem is IProtoItemDrone protoItemDrone))
            {
                // not a drone item
                Logger.Warning("Not a drone item: " + itemDrone, character);
                return;
            }

            var itemDroneControl = character.SharedGetPlayerSelectedHotbarItem();

            if (!(itemDroneControl.ProtoItem is IProtoItemDroneControl))
            {
                Logger.Info("Don't have a drone remote control selected", character);
                return;
            }

            if (SharedIsMaxDronesToControlNumberExceeded(character))
            {
                Logger.Info("Exceeded max numbers of drones to control simultaneously", character);
                return;
            }

            if (SharedIsTargetAlreadyScheduledForAnyActiveDrone(character,
                                                                worldPosition,
                                                                logError: true))
            {
                return;
            }

            if (character.GetPublicState <ICharacterPublicState>()
                .IsDead)
            {
                // dead player cannot start a drone
                return;
            }

            var protoDrone = protoItemDrone.ProtoDrone;

            if (!SharedIsValidStartLocation(character, worldPosition, out _))
            {
                Logger.Info("Too far to start a drone or has obstacles", character);
                return;
            }

            var objectDrone = itemDrone.GetPrivateState <ItemDronePrivateState>().WorldObjectDrone;

            if (!ServerIsMiningAllowed(worldPosition, objectDrone))
            {
                Logger.Info("Cannot mine there as it's already mined by another drone: " + worldPosition, character);
                return;
            }

            var targetObject = SharedGetCompatibleTarget(character,
                                                         worldPosition,
                                                         out _,
                                                         out _);

            if (targetObject is null)
            {
                Logger.Info("Nothing to mine there: " + worldPosition, character);
                return;
            }

            var durabilityValue = ItemDurabilitySystem.SharedGetDurabilityValue(itemDrone);

            if (durabilityValue == 0)
            {
                Logger.Warning("Cannot start a drone with 0 durability: " + itemDrone, character);
                return;
            }

            var dronePublicState     = objectDrone.GetPublicState <DronePublicState>();
            var droneStructurePoints = durabilityValue
                                       * protoItemDrone.DurabilityToStructurePointsConversionCoefficient;

            dronePublicState.StructurePointsCurrent = (float)droneStructurePoints;

            var controlledDrones = character.SharedGetCurrentlyControlledDrones();

            controlledDrones.Add(objectDrone);
            Logger.Info("Drone control started: " + objectDrone);

            // move drone from player inventory to its own storage items container
            var isFromHotbarContainer = itemDrone.Container == character.SharedGetPlayerContainerHotbar();
            var fromSlotIndex         = itemDrone.ContainerSlotId;

            Server.Items.MoveOrSwapItem(itemDrone,
                                        protoDrone.ServerGetStorageItemsContainer(objectDrone),
                                        out _);

            //Logger.Dev($"Drone start moving: {itemDrone} to {worldPosition}");
            protoDrone.ServerStartDrone(objectDrone,
                                        character,
                                        isFromHotbarContainer,
                                        fromSlotIndex);

            protoDrone.ServerSetDroneTarget(objectDrone,
                                            targetObject,
                                            fromStartPosition: character.Position);

            ServerItemUseObserver.NotifyItemUsed(character, itemDroneControl);
        }