コード例 #1
0
        public override void ServerOnDestroy(IDynamicWorldObject gameObject)
        {
            base.ServerOnDestroy(gameObject);

            // try drop extra containers on the ground
            var privateState = GetPrivateState(gameObject);

            DropItemsToTheGround(privateState.EquipmentItemsContainer);
            //DropItemsToTheGround(privateState.FuelItemsContainer); // consider fuel items were destroyed during the explosion

            void DropItemsToTheGround(IItemsContainer itemsContainer)
            {
                if (itemsContainer?.OccupiedSlotsCount == 0)
                {
                    return;
                }

                var groundContainer = ObjectGroundItemsContainer.ServerTryDropOnGroundContainerContent(
                    gameObject.Tile,
                    itemsContainer);

                if (groundContainer != null)
                {
                    // set custom timeout for the dropped ground items container
                    ObjectGroundItemsContainer.ServerSetDestructionTimeout(
                        (IStaticWorldObject)groundContainer.Owner,
                        DestroyedCargoDroppedItemsDestructionTimeout.TotalSeconds);
                }
            }
        }
コード例 #2
0
        public static CreateItemResult TryDropToGround(
            IReadOnlyDropItemsList dropItemsList,
            Vector2Ushort tilePosition,
            double probabilityMultiplier,
            DropItemContext context,
            [CanBeNull] out IItemsContainer groundContainer)
        {
            // obtain the ground container to drop the items into
            var tile = Api.Server.World.GetTile(tilePosition);

            groundContainer = ObjectGroundItemsContainer
                              .ServerTryGetOrCreateGroundContainerAtTileOrNeighbors(tile);

            if (groundContainer == null)
            {
                // cannot drop because there are no free space available on the ground
                return(new CreateItemResult()
                {
                    IsEverythingCreated = false
                });
            }

            var result = dropItemsList.TryDropToContainer(groundContainer, context, probabilityMultiplier);

            if (result.TotalCreatedCount == 0 &&
                groundContainer.OccupiedSlotsCount == 0)
            {
                // nothing is spawned, the ground container should be destroyed
                Api.Server.World.DestroyObject(groundContainer.OwnerAsStaticObject);
                groundContainer = null;
            }

            return(result);
        }
コード例 #3
0
        public override void ServerOnDestroy(IStaticWorldObject gameObject)
        {
            base.ServerOnDestroy(gameObject);

            LandClaimSystem.ServerOnObjectLandClaimDestroyed(gameObject);

            // try drop items from the safe storage
            var itemsContainer = GetPrivateState(gameObject).ItemsContainer;

            if (itemsContainer.OccupiedSlotsCount == 0)
            {
                // no items to drop
                return;
            }

            var groundContainer = ObjectGroundItemsContainer.ServerTryDropOnGroundContainerContent(
                gameObject.OccupiedTile,
                itemsContainer);

            if (groundContainer == null)
            {
                // no items dropped
                return;
            }

            // set custom timeout for the dropped ground items container
            ObjectGroundItemsContainer.ServerSetDestructionTimeout(
                (IStaticWorldObject)groundContainer.Owner,
                DestroyedLandClaimDroppedItemsDestructionTimeout.TotalSeconds);
        }
コード例 #4
0
        public static void ServerTrySpawnItemToCharacterOrGround(
            ICharacter character,
            IProtoItem protoItem,
            uint countToSpawn,
            ref IItemsContainer groundContainer)
        {
            var result = character.ProtoCharacter.ServerCreateItem(character, protoItem, countToSpawn);

            if (result.IsEverythingCreated)
            {
                // successfully spawned at character
                return;
            }

            countToSpawn -= result.TotalCreatedCount;

            // cannot spawn - try spawn to the ground
            if (groundContainer is null)
            {
                groundContainer = ObjectGroundItemsContainer.ServerTryGetOrCreateGroundContainerAtTileOrNeighbors(
                    character,
                    character.Tile);

                if (groundContainer is null)
                {
                    return;
                }
            }

            ServerItemsService.CreateItem(protoItem,
                                          groundContainer,
                                          count: countToSpawn);
        }
コード例 #5
0
ファイル: ItemBottleEmpty.cs プロジェクト: peter-r-g/CryoFall
        public static void ServerSpawnEmptyBottle(ICharacter character, ushort count = 1)
        {
            var createItemResult = Server.Items.CreateItem <ItemBottleEmpty>(character, count);

            if (createItemResult.IsEverythingCreated)
            {
                // notify the owner about the spawned empty bottle
                NotificationSystem.ServerSendItemsNotification(character, createItemResult);
                return;
            }

            createItemResult.Rollback();

            var groundContainer =
                ObjectGroundItemsContainer.ServerTryGetOrCreateGroundContainerAtTileOrNeighbors(character, character.Tile);

            if (groundContainer is null)
            {
                return;
            }

            createItemResult = Server.Items.CreateItem <ItemBottleEmpty>(groundContainer, count);
            if (createItemResult.IsEverythingCreated)
            {
                // notify the owner about the spawned empty bottle
                NotificationSystem.ServerSendNotificationNoSpaceInInventoryItemsDroppedToGround(
                    character,
                    createItemResult.ItemAmounts.FirstOrDefault().Key?.ProtoItem);
                return;
            }

            // BUG: cannot spawn an empty bottle either to player or to the ground. It's a rare case, but still possible.
            // It's better to return a false result and cancel the action such as drinking of the water.
            NotificationSystem.ServerSendNotificationNoSpaceInInventory(character);
        }
コード例 #6
0
        public override void ServerOnDestroy(IStaticWorldObject gameObject)
        {
            base.ServerOnDestroy(gameObject);

            ObjectGroundItemsContainer.ServerTryDropOnGroundContainerContent(
                gameObject.OccupiedTile,
                GetPrivateState(gameObject).InputItemsCointainer);
        }
コード例 #7
0
ファイル: ProtoObjectCrate.cs プロジェクト: jashking/CryoFall
        public override void ServerOnDestroy(IStaticWorldObject gameObject)
        {
            base.ServerOnDestroy(gameObject);

            ObjectGroundItemsContainer.ServerTryDropOnGroundContainerContent(
                gameObject.OccupiedTile,
                GetPrivateState(gameObject).ItemsContainer,
                destroyTimeout: DestroyedCrateDroppedItemsDestructionTimeout.TotalSeconds);
        }
コード例 #8
0
        public override void ServerOnDestroy(IItem gameObject)
        {
            base.ServerOnDestroy(gameObject);

            ICharacter character = gameObject.Container.OwnerAsCharacter;

            if (character is null)
            {
                return;
            }

            var playerPrivateState = PlayerCharacter.GetPrivateState(character);

            IItemsContainer objectGroundContainer = null;

            objectGroundContainer = ObjectPlayerLootContainer.ServerTryCreateLootContainer(character);

            if (objectGroundContainer is not null)
            {
                int slotCount = this.GetGroundSlotCount(playerPrivateState);

                // set slots count matching the total occupied slots count
                Server.Items.SetSlotsCount(objectGroundContainer,
                                           (byte)Math.Min(byte.MaxValue, objectGroundContainer.OccupiedSlotsCount + slotCount));
            }

            if (objectGroundContainer is null)
            {
                objectGroundContainer = ObjectGroundItemsContainer.ServerTryGetOrCreateGroundContainerAtTileOrNeighbors(character, character.Tile);

                if (objectGroundContainer is null)
                {
                    return;
                }
            }

            for (byte i = PlayerConstants.InventorySlotsCount; i < playerPrivateState.ContainerInventory.SlotsCount; i++)
            {
                IItem itemToDrop = playerPrivateState.ContainerInventory.GetItemAtSlot(i);
                if (itemToDrop is not null)
                {
                    Server.Items.MoveOrSwapItem(itemToDrop, objectGroundContainer, out _);
                }
            }

            if (playerPrivateState.ContainerInventory.SlotsCount != PlayerConstants.InventorySlotsCount)
            {
                Api.Server.Items.SetSlotsCount(playerPrivateState.ContainerInventory, PlayerConstants.InventorySlotsCount);
            }

            WorldObjectClaimSystem.ServerTryClaim(objectGroundContainer.OwnerAsStaticObject,
                                                  character,
                                                  durationSeconds: objectGroundContainer.OwnerAsStaticObject.ProtoStaticWorldObject
                                                  is ObjectPlayerLootContainer
                               ? ObjectPlayerLootContainer.AutoDestroyTimeoutSeconds + (10 * 60)
                               : WorldObjectClaimDuration.DroppedGoods);
        }
コード例 #9
0
        private void CreateGroundContainer()
        {
            var tile = Api.Server.World.GetTile(this.TilePosition);

            this.GroundContainer = ObjectGroundItemsContainer
                                   .ServerTryGetOrCreateGroundContainerAtTileOrNeighbors(this.Character, tile);

            // don't restrict the ground container space limit
            Api.Server.Items.SetSlotsCount(this.GroundContainer, byte.MaxValue);
        }
コード例 #10
0
        public static void ServerTryClaim(
            IWorldObject worldObject,
            ICharacter character,
            double durationSeconds,
            bool claimForPartyMembers = true)
        {
            if (!SharedIsEnabled)
            {
                return;
            }

            if (worldObject is null ||
                character is null ||
                character.IsNpc ||
                durationSeconds <= 0)
            {
                return;
            }

            var worldObjectPublicState = worldObject.AbstractPublicState as IWorldObjectPublicStateWithClaim;

            if (worldObjectPublicState is null)
            {
                Logger.Warning(
                    $"{worldObject} doesn't implement {nameof(IWorldObjectPublicStateWithClaim)} - cannot claim it");
                return;
            }

            var objectClaim = worldObjectPublicState.WorldObjectClaim;

            if (objectClaim is not null)
            {
                // already claimed
                WorldObjectClaim.ServerTryExtendClaim(objectClaim, character, durationSeconds);
                return;
            }

            objectClaim = Server.World.CreateLogicObject <WorldObjectClaim>();
            WorldObjectClaim.ServerSetupClaim(objectClaim,
                                              character,
                                              worldObject,
                                              durationSeconds,
                                              claimForPartyMembers);
            worldObjectPublicState.WorldObjectClaim = objectClaim;
            //Logger.Dev("World object claim added: " + worldObject);

            if (worldObject.ProtoGameObject is ObjectGroundItemsContainer)
            {
                // set custom timeout for the ground items container to ensure it cannot be removed from the world
                // before the claim expires
                ObjectGroundItemsContainer.ServerSetDestructionTimeout(
                    (IStaticWorldObject)worldObject,
                    durationSeconds);
            }
        }
コード例 #11
0
        public static void ServerOnDestroy(IStaticWorldObject tradingStation)
        {
            TradingStationsMapMarksSystem.ServerTryRemoveMark(tradingStation);

            var itemsContainer = GetPrivateState(tradingStation).StockItemsContainer;

            ObjectGroundItemsContainer.ServerTryDropOnGroundContainerContent(
                tradingStation.OccupiedTile,
                itemsContainer,
                DestroyedTradingStationDroppedItemsDestructionTimeout.TotalSeconds);
        }
コード例 #12
0
        public override void ServerOnDestroy(IItem gameObject)
        {
            base.ServerOnDestroy(gameObject);

            if (gameObject.Container is null)
            {
                return;
            }

            ICharacter character = gameObject.Container.OwnerAsCharacter;

            if (character is null)
            {
                return;
            }

            var playerPrivateState = PlayerCharacter.GetPrivateState(character);

            IItemsContainer objectGroundContainer = null;

            objectGroundContainer = ObjectPlayerLootContainer.ServerTryCreateLootContainer(character);

            var privateState = GetPrivateState(gameObject);

            if (objectGroundContainer is not null)
            {
                int slotCount = privateState.ItemsContainer.OccupiedSlotsCount;

                // set slots count matching the total occupied slots count
                Server.Items.SetSlotsCount(objectGroundContainer,
                                           (byte)Math.Min(byte.MaxValue, objectGroundContainer.OccupiedSlotsCount + slotCount));
            }

            if (objectGroundContainer is null)
            {
                objectGroundContainer = ObjectGroundItemsContainer.ServerTryGetOrCreateGroundContainerAtTileOrNeighbors(character, character.Tile);

                if (objectGroundContainer is null)
                {
                    return;
                }
            }

            Api.Server.Items.TryMoveAllItems(privateState.ItemsContainer, objectGroundContainer);

            WorldObjectClaimSystem.ServerTryClaim(objectGroundContainer.OwnerAsStaticObject,
                                                  character,
                                                  durationSeconds: objectGroundContainer.OwnerAsStaticObject.ProtoStaticWorldObject
                                                  is ObjectPlayerLootContainer
                               ? ObjectPlayerLootContainer.AutoDestroyTimeoutSeconds + (10 * 60)
                               : WorldObjectClaimDuration.DroppedGoods);
        }
コード例 #13
0
        public override void ServerOnDestroy(IStaticWorldObject gameObject)
        {
            base.ServerOnDestroy(gameObject);

            var itemsContainer = GetPrivateState(gameObject).ItemsContainer;

            if (itemsContainer.OccupiedSlotsCount > 0)
            {
                ObjectGroundItemsContainer.ServerTryDropOnGroundContainerContent(
                    gameObject.OccupiedTile,
                    itemsContainer);
            }
        }
コード例 #14
0
        public override void ServerOnDestroy(IDynamicWorldObject gameObject)
        {
            base.ServerOnDestroy(gameObject);

            // try drop extra containers on the ground
            var privateState = GetPrivateState(gameObject);

            ObjectGroundItemsContainer.ServerTryDropOnGroundContainerContent(
                gameObject.Tile,
                privateState.EquipmentItemsContainer,
                DestroyedCargoDroppedItemsDestructionTimeout.TotalSeconds);
            // consider fuel items were destroyed during the explosion
        }
コード例 #15
0
ファイル: ProtoDrone.cs プロジェクト: jashking/CryoFall
        public void ServerDropDroneToGround(IDynamicWorldObject objectDrone)
        {
            var privateState     = objectDrone.GetPrivateState <DronePrivateState>();
            var storageContainer = privateState.StorageItemsContainer;

            if (storageContainer.OccupiedSlotsCount == 0)
            {
                return;
            }

            // drop all items on the ground
            IItemsContainer groundContainer = null;

            if (privateState.CharacterOwner is not null)
            {
                groundContainer = ObjectPlayerLootContainer.ServerTryCreateLootContainer(privateState.CharacterOwner,
                                                                                         objectDrone.Position);

                if (groundContainer is not null)
                {
                    // set slots count matching the total occupied slots count
                    Server.Items.SetSlotsCount(
                        groundContainer,
                        (byte)Math.Min(byte.MaxValue,
                                       groundContainer.OccupiedSlotsCount
                                       + storageContainer.OccupiedSlotsCount));
                }
            }

            groundContainer ??=
            ObjectGroundItemsContainer.ServerTryGetOrCreateGroundContainerAtTileOrNeighbors(
                privateState.CharacterOwner,
                objectDrone.Tile);

            if (groundContainer is not null)
            {
                Server.Items.TryMoveAllItems(storageContainer, groundContainer);
                WorldObjectClaimSystem.ServerTryClaim(
                    groundContainer.OwnerAsStaticObject,
                    privateState.CharacterOwner,
                    durationSeconds: ObjectPlayerLootContainer.AutoDestroyTimeoutSeconds
                    + (10 * 60));
            }
            else
            {
                // TODO: try to create a ground container in any other ground spot
                Logger.Error("Cannot find a place to drop the drone contents on the ground - drone lost!"
                             + objectDrone);
            }
        }
コード例 #16
0
        public override void ServerOnDestroy(IStaticWorldObject gameObject)
        {
            base.ServerOnDestroy(gameObject);

            var stateGasoline = GetPrivateState(gameObject).ManufacturingStateGasoline;

            ObjectGroundItemsContainer.ServerTryDropOnGroundContainerContent(
                gameObject.OccupiedTile,
                stateGasoline.ContainerInput);

            ObjectGroundItemsContainer.ServerTryDropOnGroundContainerContent(
                gameObject.OccupiedTile,
                stateGasoline.ContainerOutput);
        }
コード例 #17
0
        public override void ServerOnDestroy(IStaticWorldObject gameObject)
        {
            base.ServerOnDestroy(gameObject);

            foreach (var reactorState in GetPrivateState(gameObject).ReactorStates)
            {
                if (reactorState?.ItemsContainer is null)
                {
                    continue;
                }

                ObjectGroundItemsContainer.ServerTryDropOnGroundContainerContent(
                    gameObject.OccupiedTile,
                    reactorState.ItemsContainer);
            }
        }
コード例 #18
0
        public override void ServerOnDestroy(IStaticWorldObject gameObject)
        {
            base.ServerOnDestroy(gameObject);

            var privateState = GetPrivateState(gameObject);

            ObjectGroundItemsContainer.ServerTryDropOnGroundContainerContent(
                gameObject.OccupiedTile,
                privateState.ManufacturingState?.ContainerInput);

            ObjectGroundItemsContainer.ServerTryDropOnGroundContainerContent(
                gameObject.OccupiedTile,
                privateState.ManufacturingState?.ContainerOutput);

            ObjectGroundItemsContainer.ServerTryDropOnGroundContainerContent(
                gameObject.OccupiedTile,
                privateState.FuelBurningState?.ContainerFuel);
        }
コード例 #19
0
        public static void ServerTrySpawnItemToCharacterOrGround(
            ICharacter character,
            IProtoItem protoItem,
            uint countToSpawn,
            ref IItemsContainer groundContainer)
        {
            var result = character.ProtoCharacter.ServerCreateItem(character, protoItem, countToSpawn);

            if (result.IsEverythingCreated)
            {
                // successfully spawned at character
                return;
            }

            countToSpawn -= result.TotalCreatedCount;

            // cannot spawn - try spawn to the ground
            if (groundContainer == null)
            {
                groundContainer = ObjectGroundItemsContainer.ServerTryGetOrCreateGroundContainerAtTileOrNeighbors(
                    character.Tile);

                if (groundContainer == null)
                {
                    // cannot drop on ground
                    return;
                }

                ServerItemsService.SetContainerType <ItemsContainerOutputPublic>(groundContainer);
            }

            // estimate how many slots are required
            var groundSlotsCount = (int)groundContainer.OccupiedSlotsCount;

            groundSlotsCount += (int)Math.Ceiling(countToSpawn / (double)protoItem.MaxItemsPerStack);
            ServerItemsService.SetSlotsCount(
                groundContainer,
                (byte)Math.Min(byte.MaxValue, groundSlotsCount));

            ServerItemsService.CreateItem(protoItem,
                                          groundContainer,
                                          count: countToSpawn);
        }
コード例 #20
0
        public static void ServerTrySpawnItemToCharacterOrGround(
            ICharacter character,
            IProtoItem protoItem,
            uint countToSpawn,
            ref IItemsContainer groundContainer)
        {
            var result = Server.Items.CreateItem(protoItem, character, countToSpawn);

            if (result.IsEverythingCreated)
            {
                // successfully spawned at character
                return;
            }

            countToSpawn -= result.TotalCreatedCount;

            // cannot spawn - try spawn to the ground
            if (groundContainer is null)
            {
                groundContainer = ObjectGroundItemsContainer.ServerTryGetOrCreateGroundContainerAtTileOrNeighbors(
                    character,
                    character.Tile);

                if (groundContainer is null)
                {
                    return;
                }
            }

            ServerItemsService.CreateItem(protoItem,
                                          groundContainer,
                                          count: countToSpawn);

            if (groundContainer.OccupiedSlotsCount > 0)
            {
                return;
            }

            // nothing is spawned, the ground container should be destroyed
            Server.World.DestroyObject(groundContainer.OwnerAsStaticObject);
            groundContainer = null;
        }
コード例 #21
0
        /// <summary>
        /// Server spawn callback for item.
        /// </summary>
        /// <param name="trigger">Trigger leading to this spawn.</param>
        /// <param name="zone">Server zone instance.</param>
        /// <param name="protoItem">Prototype of item to spawn.</param>
        /// <param name="tilePosition">Position to try spawn at.</param>
        protected IGameObjectWithProto ServerSpawnItem(
            IProtoTrigger trigger,
            IServerZone zone,
            IProtoItem protoItem,
            Vector2Ushort tilePosition)
        {
            var container = ObjectGroundItemsContainer.ServerTryGetOrCreateGroundContainerAtTile(
                tilePosition,
                writeWarningsToLog: false);

            if (container == null)
            {
                // cannot spawn item there
                return(null);
            }

            return(Server.Items.CreateItem(protoItem, container)
                   .ItemAmounts
                   .Keys
                   .FirstOrDefault());
        }
コード例 #22
0
        public static void ServerOnDestroy(IStaticWorldObject tradingStation)
        {
            TradingStationsMapMarksSystem.ServerTryRemoveMark(tradingStation);

            var itemsContainer = GetPrivateState(tradingStation).StockItemsContainer;

            if (itemsContainer.OccupiedSlotsCount == 0)
            {
                return;
            }

            var groundContainer = ObjectGroundItemsContainer.ServerTryDropOnGroundContainerContent(
                tradingStation.OccupiedTile,
                itemsContainer);

            if (groundContainer != null)
            {
                // set custom timeout for the dropped ground items container
                ObjectGroundItemsContainer.ServerSetDestructionTimeout(
                    (IStaticWorldObject)groundContainer.Owner,
                    DestroyedTradingStationDroppedItemsDestructionTimeout.TotalSeconds);
            }
        }
コード例 #23
0
        public override void ServerOnDestroy(IStaticWorldObject gameObject)
        {
            base.ServerOnDestroy(gameObject);

            var itemsContainer = GetPrivateState(gameObject).ItemsContainer;

            if (itemsContainer.OccupiedSlotsCount == 0)
            {
                return;
            }

            var groundContainer = ObjectGroundItemsContainer.ServerTryDropOnGroundContainerContent(
                gameObject.OccupiedTile,
                itemsContainer);

            if (groundContainer != null)
            {
                // set custom timeout for the dropped ground items container
                ObjectGroundItemsContainer.ServerSetDestructionTimeout(
                    (IStaticWorldObject)groundContainer.Owner,
                    DestroyedCrateDroppedItemsDestructionTimeout.TotalSeconds);
            }
        }
コード例 #24
0
        private static bool CheckCase10(Context c)
        {
            if (!c.IsAltHeld)
            {
                return(false);
            }

            if (c.IsFullSlot &&
                c.IsLeftMouseButton)
            {
                TryUseItem(c.ItemInSlot);
            }
            else if (c.IsRightMouseButton)
            {
                ObjectGroundItemsContainer.ClientTryDropItemOnGround(
                    c.ItemInSlot,
                    countToDrop: c.IsControlHeld
                                     ? (ushort)1
                                     : c.ItemInSlot.Count);
            }

            // even if we don't execute the event we need to be sure no other item management cases handled as Alt key is held
            return(true);
        }
コード例 #25
0
        /// <summary>
        /// Executed when a weapon must reload (after the reloading duration is completed).
        /// </summary>
        private static void SharedProcessWeaponReload(
            ICharacter character,
            WeaponState weaponState,
            out bool isAmmoTypeChanged)
        {
            var weaponReloadingState = weaponState.WeaponReloadingState;

            // remove weapon reloading state
            weaponState.WeaponReloadingState = null;

            var itemWeapon             = weaponReloadingState.Item;
            var itemWeaponProto        = (IProtoItemWeapon)itemWeapon.ProtoGameObject;
            var itemWeaponPrivateState = itemWeapon.GetPrivateState <WeaponPrivateState>();
            var weaponAmmoCount        = (int)itemWeaponPrivateState.AmmoCount;
            var weaponAmmoCapacity     = itemWeaponProto.AmmoCapacity;

            isAmmoTypeChanged = false;

            var selectedProtoItemAmmo = weaponReloadingState.ProtoItemAmmo;
            var currentProtoItemAmmo  = itemWeaponPrivateState.CurrentProtoItemAmmo;

            if (weaponAmmoCount > 0)
            {
                if (selectedProtoItemAmmo != currentProtoItemAmmo &&
                    weaponAmmoCount > 0)
                {
                    // unload current ammo
                    if (IsServer)
                    {
                        var targetContainers =
                            SharedGetTargetContainersForCharacterAmmo(character, isForAmmoUnloading: true);
                        var result = Server.Items.CreateItem(
                            protoItem: currentProtoItemAmmo,
                            new AggregatedItemsContainers(targetContainers),
                            count: (ushort)weaponAmmoCount);

                        if (!result.IsEverythingCreated)
                        {
                            // cannot unload current ammo - no space, try to unload to the ground
                            result.Rollback();

                            var tile            = Api.Server.World.GetTile(character.TilePosition);
                            var groundContainer = ObjectGroundItemsContainer
                                                  .ServerTryGetOrCreateGroundContainerAtTileOrNeighbors(character, tile);

                            if (groundContainer is null)
                            {
                                // cannot unload current ammo to the ground - no free space around character
                                NotificationSystem.ServerSendNotificationNoSpaceInInventory(character);
                                return;
                            }

                            result = Server.Items.CreateItem(
                                container: groundContainer,
                                protoItem: currentProtoItemAmmo,
                                count: (ushort)weaponAmmoCount);

                            if (!result.IsEverythingCreated)
                            {
                                // cannot unload current ammo to the ground - no space in ground containers near the character
                                result.Rollback();
                                NotificationSystem.ServerSendNotificationNoSpaceInInventory(character);
                                return;
                            }

                            // notify player that there were not enough space in inventory so the items were dropped to the ground
                            NotificationSystem.ServerSendNotificationNoSpaceInInventoryItemsDroppedToGround(
                                character,
                                result.ItemAmounts.First().Key?.ProtoItem);
                        }
                    }

                    Logger.Info(
                        $"Weapon ammo unloaded: {itemWeapon} -> {weaponAmmoCount} {currentProtoItemAmmo})",
                        character);

                    weaponAmmoCount = 0;
                    itemWeaponPrivateState.SetAmmoCount(0);
                }
                else // if the same ammo type is loaded
                if (weaponAmmoCount == weaponAmmoCapacity)
                {
                    // already completely loaded
                    Logger.Info(
                        $"Weapon reloading cancelled: {itemWeapon} - no reloading is required ({weaponAmmoCount}/{weaponAmmoCapacity} {selectedProtoItemAmmo})",
                        character);
                    return;
                }
            }
            else // if ammoCount == 0
            if (selectedProtoItemAmmo is null &&
                currentProtoItemAmmo is null)
            {
                Logger.Info(
                    $"Weapon reloading cancelled: {itemWeapon} - already unloaded ({weaponAmmoCount}/{weaponAmmoCapacity})",
                    character);
                return;
            }

            if (selectedProtoItemAmmo != null)
            {
                var selectedAmmoGroup = SharedGetCompatibleAmmoGroups(character, itemWeaponProto)
                                        .FirstOrDefault(g => g.Key == selectedProtoItemAmmo);

                if (selectedAmmoGroup is null)
                {
                    Logger.Warning(
                        $"Weapon reloading impossible: {itemWeapon} - no ammo of the required type ({selectedProtoItemAmmo})",
                        character);
                    return;
                }

                var ammoItems = SharedSelectAmmoItemsFromGroup(selectedAmmoGroup,
                                                               ammoCountNeed: weaponAmmoCapacity - weaponAmmoCount);
                foreach (var request in ammoItems)
                {
                    var itemAmmo = request.Item;
                    Api.Assert(itemAmmo.ProtoItem == selectedProtoItemAmmo, "Sanity check");

                    int ammoToSubstract;

                    var itemAmmoCount = itemAmmo.Count;
                    if (itemAmmoCount == 0)
                    {
                        continue;
                    }

                    if (request.Count != itemAmmoCount)
                    {
                        if (request.Count < itemAmmoCount)
                        {
                            itemAmmoCount = request.Count;
                        }
                        else if (IsServer)
                        {
                            Logger.Warning(
                                $"Trying to take more ammo to reload than player have: {itemAmmo} requested {request.Count}. Will reload as much as possible only.",
                                character);
                        }
                    }

                    if (weaponAmmoCount + itemAmmoCount >= weaponAmmoCapacity)
                    {
                        // there are more than enough ammo in that item stack to fully refill the weapon
                        ammoToSubstract = weaponAmmoCapacity - weaponAmmoCount;
                        weaponAmmoCount = weaponAmmoCapacity;
                    }
                    else
                    {
                        // consume full item stack
                        ammoToSubstract  = itemAmmoCount;
                        weaponAmmoCount += itemAmmoCount;
                    }

                    // reduce ammo item count
                    if (IsServer)
                    {
                        Server.Items.SetCount(
                            itemAmmo,
                            itemAmmo.Count - ammoToSubstract,
                            byCharacter: character);
                    }

                    if (weaponAmmoCount == weaponAmmoCapacity)
                    {
                        // the weapon is fully reloaded, no need to subtract ammo from the next ammo items
                        break;
                    }
                }
            }

            if (currentProtoItemAmmo != selectedProtoItemAmmo)
            {
                // another ammo type selected
                itemWeaponPrivateState.CurrentProtoItemAmmo = selectedProtoItemAmmo;
                // reset weapon cache (it will be re-calculated on next fire processing)
                weaponState.WeaponCache = null;
                isAmmoTypeChanged       = true;
            }

            if (weaponAmmoCount < 0 ||
                weaponAmmoCount > weaponAmmoCapacity)
            {
                Logger.Error(
                    "Something is completely wrong during reloading! Result ammo count is: " + weaponAmmoCount);
                weaponAmmoCount = 0;
            }

            itemWeaponPrivateState.SetAmmoCount((ushort)weaponAmmoCount);

            if (weaponAmmoCount == 0)
            {
                // weapon unloaded - and the log entry about this is already written (see above)
                return;
            }

            Logger.Info(
                $"Weapon reloaded: {itemWeapon} - ammo {weaponAmmoCount}/{weaponAmmoCapacity} {selectedProtoItemAmmo?.ToString() ?? "<no ammo>"}",
                character);

            if (IsServer)
            {
                ServerNotifyAboutReloading(character, weaponState, isFinished: true);
            }
            else
            {
                weaponState.ProtoWeapon.SoundPresetWeapon
                .PlaySound(WeaponSound.ReloadFinished,
                           character,
                           SoundConstants.VolumeWeapon);
            }
        }
コード例 #26
0
        private void ServerGatheringSystemGatherHandler(ICharacter character, IStaticWorldObject worldObject)
        {
            if (!(worldObject.ProtoStaticWorldObject is ObjectCorpse))
            {
                return;
            }

            // corpse looted
            // find the device and vial
            var itemDevice = character.SharedGetPlayerContainerEquipment()
                             .GetItemsOfProto(this)
                             .FirstOrDefault();

            if (itemDevice is null)
            {
                // don't have an equipped device
                return;
            }

            var protoItemVialEmpty = GetProtoEntity <ItemVialEmpty>();

            // require at least one vial
            if (!character.ContainsItemsOfType(protoItemVialEmpty, requiredCount: 1))
            {
                // don't have an empty vial
                this.CallClient(character, _ => _.ClientRemote_NotEnoughEmptyVials());
                return;
            }

            var protoMob = worldObject.GetPublicState <ObjectCorpse.PublicState>()
                           .ProtoCharacterMob;
            var healthMax     = protoMob.StatDefaultHealthMax;
            var maxVialsToUse = (ushort)MathHelper.Clamp(
                Math.Floor(protoMob.BiomaterialValueMultiplier * healthMax / 100.0),
                min: 1,
                max: 10);

            // destroy empty vial
            Server.Items.DestroyItemsOfType(
                character,
                protoItemVialEmpty,
                maxVialsToUse,
                out var destroyedEmptyVialsCount);

            if (destroyedEmptyVialsCount == 0)
            {
                // cannot destroy any empty vial (should be impossible)
                return;
            }

            // spawn biomaterial vials
            var protoItemVialBiomaterial = Api.GetProtoEntity <ItemVialBiomaterial>();
            var createItemResult         = Server.Items.CreateItem(protoItemVialBiomaterial,
                                                                   character,
                                                                   count: destroyedEmptyVialsCount);

            if (!createItemResult.IsEverythingCreated)
            {
                createItemResult.Rollback();
                var groundItemsContainer =
                    ObjectGroundItemsContainer.ServerTryGetOrCreateGroundContainerAtTileOrNeighbors(character,
                                                                                                    character.Tile);
                if (groundItemsContainer is null)
                {
                    Logger.Error("Not enough space around the player to drop a full vial");
                    // restore items
                    Server.Items.CreateItem(protoItemVialEmpty,
                                            character,
                                            destroyedEmptyVialsCount);
                    return;
                }

                createItemResult = Server.Items.CreateItem(protoItemVialBiomaterial,
                                                           groundItemsContainer,
                                                           count: destroyedEmptyVialsCount);
                if (!createItemResult.IsEverythingCreated)
                {
                    createItemResult.Rollback();
                    Logger.Error("Not enough space around the player to drop a full vial");
                    // restore items
                    Server.Items.CreateItem(protoItemVialEmpty,
                                            character,
                                            destroyedEmptyVialsCount);
                    return;
                }

                NotificationSystem.ServerSendNotificationNoSpaceInInventoryItemsDroppedToGround(character,
                                                                                                protoItemVialBiomaterial);
            }

            var itemChangedCount = NotificationSystem.SharedGetItemsChangedCount(createItemResult);

            itemChangedCount.Add(protoItemVialEmpty, -(int)destroyedEmptyVialsCount);
            NotificationSystem.ServerSendItemsNotification(character, itemChangedCount);

            ItemDurabilitySystem.ServerModifyDurability(itemDevice, -DurabilityDecreasePerUse);
            Logger.Info("Biomaterial collected successfully with Biomaterial collector", character);
        }
コード例 #27
0
        /// <summary>
        /// Get all (already populated) areas with their objects inside the zone
        /// </summary>
        private async Task ServerFillSpawnAreasInZoneAsync(
            IServerZone zone,
            IReadOnlyDictionary <Vector2Ushort, SpawnZoneArea> areas,
            Func <Task> callbackYieldIfOutOfTime)
        {
            // this is a heavy method so we will try to yield every 100 objects to reduce the load
            const int defaultCounterToYieldValue = 100;
            var       counterToYield             = defaultCounterToYieldValue;

            // this check has a problem - it returns only objects strictly inside the zone,
            // but we also need to consider objects nearby the zone for restriction presets
            //await zone.PopulateStaticObjectsInZone(tempList, callbackYieldIfOutOfTime);

            TempListAllStaticWorldObjects.Clear();
            await Api.Server.World.GetStaticWorldObjectsAsync(TempListAllStaticWorldObjects);

            foreach (var staticObject in TempListAllStaticWorldObjects)
            {
                await YieldIfOutOfTime();

                if (staticObject.IsDestroyed)
                {
                    continue;
                }

                var position = staticObject.TilePosition;
                var area     = GetArea(position, isMobTrackingEnumeration: false);
                if (area is null)
                {
                    continue;
                }

                var protoStaticWorldObject = staticObject.ProtoStaticWorldObject;
                if (!(protoStaticWorldObject is ObjectGroundItemsContainer))
                {
                    if (protoStaticWorldObject.IsIgnoredBySpawnScripts)
                    {
                        // we don't consider padding to certain objects such as ground decals
                        // (though they still might affect spawn during the tiles check)
                        continue;
                    }

                    // create entry for regular static object
                    var preset = this.FindPreset(protoStaticWorldObject);
                    if (preset != null &&
                        preset.Density > 0 &&
                        !zone.IsContainsPosition(staticObject.TilePosition))
                    {
                        // this object is a part of the preset spawn list but it's not present in the zone
                        // don't consider this object
                        // TODO: this might cause a problem if there is a padding to this object check
                        continue;
                    }

                    area.Add(preset, position);
                    continue;
                }

                // ground container object
                var itemsContainer = ObjectGroundItemsContainer.GetPublicState(staticObject).ItemsContainer;
                foreach (var item in itemsContainer.Items)
                {
                    // create entry for each item in the ground container
                    var preset = this.FindPreset(item.ProtoItem);
                    if (preset != null &&
                        preset.Density > 0 &&
                        !zone.IsContainsPosition(staticObject.TilePosition))
                    {
                        // this object is a part of the preset spawn list but it's not present in the zone
                        // don't consider this object
                        continue;
                    }

                    area.Add(preset, position);
                }
            }

            var mobsTrackingManager = SpawnedMobsTrackingManagersStore.Get(this, zone);

            foreach (var mob in mobsTrackingManager.EnumerateAll())
            {
                await YieldIfOutOfTime();

                var position = mob.TilePosition;
                var area     = GetArea(position, isMobTrackingEnumeration: true);
                area?.Add(this.FindPreset(mob.ProtoCharacter), position);
            }

            return;

            SpawnZoneArea GetArea(Vector2Ushort tilePosition, bool isMobTrackingEnumeration)
            {
                var zoneChunkStartPosition = SpawnZoneArea.CalculateStartPosition(tilePosition);

                if (areas.TryGetValue(zoneChunkStartPosition, out var area))
                {
                    return(area);
                }

                if (isMobTrackingEnumeration)
                {
                    return(null);
                }

                return(null);
//                throw new Exception("No zone area found for " + tilePosition);

                //var newArea = new SpawnZoneArea(zoneChunkStartPosition, zoneChunk);
                //areas.Add(newArea);
                //return newArea;
            }

            Task YieldIfOutOfTime()
            {
                if (--counterToYield > 0)
                {
                    return(Task.CompletedTask);
                }

                counterToYield = defaultCounterToYieldValue;
                return(callbackYieldIfOutOfTime());
            }
        }
コード例 #28
0
        /// <summary>
        /// Get all (already populated) areas with their objects inside the zone
        /// </summary>
        private async Task ServerFillSpawnAreasInZoneAsync(
            IServerZone zone,
            IReadOnlyDictionary <Vector2Ushort, SpawnZoneArea> areas,
            Func <Task> callbackYieldIfOutOfTime)
        {
            // this is a heavy method so we will try to yield every 100 objects to reduce the load
            const int defaultCounterToYieldValue = 100;
            var       counterToYield             = defaultCounterToYieldValue;

            using (var tempList = Api.Shared.GetTempList <IStaticWorldObject>())
            {
                await zone.PopulateStaticObjectsInZone(tempList, callbackYieldIfOutOfTime);

                foreach (var staticObject in tempList)
                {
                    await YieldIfOutOfTime();

                    if (staticObject.IsDestroyed)
                    {
                        continue;
                    }

                    var position = staticObject.TilePosition;
                    var area     = GetArea(position, isMobTrackingEnumeration: false);
                    var protoStaticWorldObject = staticObject.ProtoStaticWorldObject;
                    if (!(protoStaticWorldObject is ObjectGroundItemsContainer))
                    {
                        if (protoStaticWorldObject.Kind == StaticObjectKind.FloorDecal)
                        {
                            // we don't consider padding to decal objects
                            // (though they still might affect spawn during tile check)
                            continue;
                        }

                        // create entry for regular static object
                        area.Add(this.FindPreset(protoStaticWorldObject), position);
                        continue;
                    }

                    // ground container object
                    var itemsContainer = ObjectGroundItemsContainer.GetPublicState(staticObject).ItemsContainer;
                    foreach (var item in itemsContainer.Items)
                    {
                        // create entry for each item in the ground container
                        area.Add(this.FindPreset(item.ProtoItem), position);
                    }
                }
            }

            var mobsTrackingManager = SpawnedMobsTrackingManagersStore.Get(this, zone);

            foreach (var mob in mobsTrackingManager.EnumerateAll())
            {
                await YieldIfOutOfTime();

                var position = mob.TilePosition;
                var area     = GetArea(position, isMobTrackingEnumeration: true);
                area?.Add(this.FindPreset(mob.ProtoCharacter), position);
            }

            return;

            SpawnZoneArea GetArea(Vector2Ushort tilePosition, bool isMobTrackingEnumeration)
            {
                var zoneChunkStartPosition = SpawnZoneArea.CalculateStartPosition(tilePosition);

                if (areas.TryGetValue(zoneChunkStartPosition, out var area))
                {
                    return(area);
                }

                if (isMobTrackingEnumeration)
                {
                    return(null);
                }

                throw new Exception("No zone area found for " + tilePosition);

                //var newArea = new SpawnZoneArea(zoneChunkStartPosition, zoneChunk);
                //areas.Add(newArea);
                //return newArea;
            }

            Task YieldIfOutOfTime()
            {
                if (--counterToYield > 0)
                {
                    return(Task.CompletedTask);
                }

                counterToYield = defaultCounterToYieldValue;
                return(callbackYieldIfOutOfTime());
            }
        }
コード例 #29
0
ファイル: ProtoDrone.cs プロジェクト: jashking/CryoFall
        private static void ServerOnDroneReturnedToPlayer(IDynamicWorldObject worldObject)
        {
            var privateState = GetPrivateState(worldObject);
            var character    = privateState.CharacterOwner;

            CharacterDroneControlSystem.ServerDespawnDrone(worldObject, isReturnedToPlayer: true);

            var storageContainer = privateState.StorageItemsContainer;

            if (storageContainer.OccupiedSlotsCount == 0)
            {
                return;
            }

            // drop storage container contents to player
            // but first, move the drone item to its original slot (if possible)
            var characterContainerInventory = character.SharedGetPlayerContainerInventory();
            var characterContainerHotbar    = character.SharedGetPlayerContainerHotbar();

            var itemInFirstSlot = storageContainer.GetItemAtSlot(0);

            if (itemInFirstSlot is not null)
            {
                // item in the first slot is the drone's associated item
                // it could be destroyed in case the drone's HP dropped <= 1
                Server.Items.MoveOrSwapItem(itemInFirstSlot,
                                            privateState.IsStartedFromHotbarContainer
                                                ? characterContainerHotbar
                                                : characterContainerInventory,
                                            slotId: privateState.StartedFromSlotIndex,
                                            movedCount: out _);
            }

            var result = Server.Items.TryMoveAllItems(storageContainer, characterContainerInventory);

            try
            {
                if (storageContainer.OccupiedSlotsCount == 0)
                {
                    // all items moved from drone to player
                    return;
                }

                // try move remaining items to hotbar
                var resultToHotbar = Server.Items.TryMoveAllItems(storageContainer, characterContainerHotbar);
                result.MergeWith(resultToHotbar,
                                 areAllItemsMoved: resultToHotbar.AreAllItemMoved);

                if (storageContainer.OccupiedSlotsCount == 0)
                {
                    // all items moved from drone to player
                    return;
                }
            }
            finally
            {
                if (result.MovedItems.Count > 0)
                {
                    // notify player about the received items
                    NotificationSystem.ServerSendItemsNotification(
                        character,
                        result.MovedItems
                        .GroupBy(p => p.Key.ProtoItem)
                        .Where(p => !(p.Key is TItemDrone))
                        .ToDictionary(p => p.Key, p => p.Sum(v => v.Value)));
                }
            }

            // try to drop the remaining items on the ground
            var groundContainer = ObjectGroundItemsContainer
                                  .ServerTryGetOrCreateGroundContainerAtTileOrNeighbors(character, character.Tile);

            if (groundContainer is not null)
            {
                var result2 = Server.Items.TryMoveAllItems(storageContainer, groundContainer);
                if (result2.MovedItems.Count > 0)
                {
                    var protoItemForIcon = result2.MovedItems.First().Key.ProtoItem;

                    NotificationSystem.ServerSendNotificationNoSpaceInInventoryItemsDroppedToGround(
                        character,
                        protoItemForIcon);

                    // ensure that these items could be lifted only by their owner in PvE
                    WorldObjectClaimSystem.ServerTryClaim(groundContainer.OwnerAsStaticObject,
                                                          character,
                                                          WorldObjectClaimDuration.DroppedGoods);
                }
            }

            if (storageContainer.OccupiedSlotsCount == 0)
            {
                return;
            }

            Logger.Error("Not all items dropped on the ground from the drone storage: "
                         + worldObject
                         + " slots occupied: "
                         + storageContainer.OccupiedSlotsCount);
        }
コード例 #30
0
        /// <summary>
        /// Executed when a weapon must reload (after the reloading duration is completed).
        /// </summary>
        private static void SharedProcessWeaponReload(ICharacter character, WeaponState weaponState)
        {
            var weaponReloadingState = weaponState.WeaponReloadingState;

            // remove weapon reloading state
            weaponState.WeaponReloadingState = null;

            var itemWeapon             = weaponReloadingState.Item;
            var itemWeaponProto        = (IProtoItemWeapon)itemWeapon.ProtoGameObject;
            var itemWeaponPrivateState = itemWeapon.GetPrivateState <WeaponPrivateState>();
            var weaponAmmoCount        = (int)itemWeaponPrivateState.AmmoCount;
            var weaponAmmoCapacity     = itemWeaponProto.AmmoCapacity;

            var selectedProtoItemAmmo = weaponReloadingState.ProtoItemAmmo;
            var currentProtoItemAmmo  = itemWeaponPrivateState.CurrentProtoItemAmmo;

            if (weaponAmmoCount > 0)
            {
                if (selectedProtoItemAmmo != currentProtoItemAmmo &&
                    weaponAmmoCount > 0)
                {
                    // unload current ammo
                    if (IsServer)
                    {
                        var result = Server.Items.CreateItem(
                            toCharacter: character,
                            protoItem: currentProtoItemAmmo,
                            count: (ushort)weaponAmmoCount);

                        if (!result.IsEverythingCreated)
                        {
                            // cannot unload current ammo - no space, try to unload to the ground
                            result.Rollback();

                            var tile            = Api.Server.World.GetTile(character.TilePosition);
                            var groundContainer = ObjectGroundItemsContainer
                                                  .ServerTryGetOrCreateGroundContainerAtTileOrNeighbors(tile);

                            if (groundContainer == null)
                            {
                                // cannot unload current ammo to the ground - no free space around character
                                Instance.CallClient(character,
                                                    _ => _.ClientRemote_NoSpaceForUnloadedAmmo(currentProtoItemAmmo));
                                return;
                            }

                            result = Server.Items.CreateItem(
                                container: groundContainer,
                                protoItem: currentProtoItemAmmo,
                                count: (ushort)weaponAmmoCount);

                            if (!result.IsEverythingCreated)
                            {
                                // cannot unload current ammo to the ground - no space in ground containers near the character
                                result.Rollback();
                                Instance.CallClient(character,
                                                    _ => _.ClientRemote_NoSpaceForUnloadedAmmo(
                                                        currentProtoItemAmmo));
                                return;
                            }

                            // notify player that there were not enough space in inventory so the items were dropped to the ground
                            NotificationSystem.ServerSendNotificationNoSpaceInInventoryItemsDroppedToGround(
                                character,
                                result.ItemAmounts.First().Key?.ProtoItem);
                        }
                    }

                    Logger.Info(
                        $"Weapon ammo unloaded: {itemWeapon} -> {weaponAmmoCount} {currentProtoItemAmmo})",
                        character);

                    weaponAmmoCount = 0;
                    itemWeaponPrivateState.AmmoCount = 0;
                }
                else // if the same ammo type is loaded
                if (weaponAmmoCount == weaponAmmoCapacity)
                {
                    // already completely loaded
                    Logger.Info(
                        $"Weapon reloading cancelled: {itemWeapon} - no reloading is required ({weaponAmmoCount}/{weaponAmmoCapacity} {selectedProtoItemAmmo})",
                        character);
                    return;
                }
            }
            else // if ammoCount == 0
            if (selectedProtoItemAmmo == null &&
                currentProtoItemAmmo == null)
            {
                Logger.Info(
                    $"Weapon reloading cancelled: {itemWeapon} - already unloaded ({weaponAmmoCount}/{weaponAmmoCapacity})",
                    character);
                return;
            }

            if (selectedProtoItemAmmo != null)
            {
                var selectedAmmoGroup = SharedGetCompatibleAmmoGroups(character, itemWeaponProto)
                                        .FirstOrDefault(g => g.Key == selectedProtoItemAmmo);

                if (selectedAmmoGroup == null)
                {
                    Logger.Warning(
                        $"Weapon reloading impossible: {itemWeapon} - no ammo of the required type ({selectedProtoItemAmmo})",
                        character);
                    return;
                }

                var ammoItems = SharedSelectAmmoItemsFromGroup(selectedAmmoGroup,
                                                               ammoCountNeed: weaponAmmoCapacity - weaponAmmoCount);
                foreach (var request in ammoItems)
                {
                    var itemAmmo = request.Item;
                    if (itemAmmo.ProtoItem != selectedProtoItemAmmo)
                    {
                        Logger.Error(
                            "Trying to load multiple ammo types in one reloading: "
                            + ammoItems.Select(a => a.Item.ProtoItem).GetJoinedString(),
                            character);
                        break;
                    }

                    int ammoToSubstract;

                    var itemAmmoCount = itemAmmo.Count;
                    if (itemAmmoCount == 0)
                    {
                        continue;
                    }

                    if (request.Count != itemAmmoCount)
                    {
                        if (request.Count < itemAmmoCount)
                        {
                            itemAmmoCount = request.Count;
                        }
                        else if (IsServer)
                        {
                            Logger.Warning(
                                $"Trying to take more ammo to reload than player have: {itemAmmo} requested {request.Count}. Will reload as much as possible only.",
                                character);
                        }
                    }

                    if (weaponAmmoCount + itemAmmoCount >= weaponAmmoCapacity)
                    {
                        // there are more than enough ammo in that item stack to fully refill the weapon
                        ammoToSubstract = weaponAmmoCapacity - weaponAmmoCount;
                        weaponAmmoCount = weaponAmmoCapacity;
                    }
                    else
                    {
                        // consume full item stack
                        ammoToSubstract  = itemAmmoCount;
                        weaponAmmoCount += itemAmmoCount;
                    }

                    // check if character owns this item
                    if (itemAmmo.Container.OwnerAsCharacter != character)
                    {
                        Logger.Error("The character doesn't own " + itemAmmo + " - cannot use it to reload",
                                     character);
                        continue;
                    }

                    // reduce ammo item count
                    if (IsServer)
                    {
                        Server.Items.SetCount(
                            itemAmmo,
                            itemAmmo.Count - ammoToSubstract,
                            byCharacter: character);
                    }

                    if (weaponAmmoCount == weaponAmmoCapacity)
                    {
                        // the weapon is fully reloaded, no need to subtract ammo from the next ammo items
                        break;
                    }
                }
            }

            if (currentProtoItemAmmo != selectedProtoItemAmmo)
            {
                // another ammo type selected
                itemWeaponPrivateState.CurrentProtoItemAmmo = selectedProtoItemAmmo;
                // reset weapon cache (it will be re-calculated on next fire processing)
                weaponState.WeaponCache = null;
            }

            if (weaponAmmoCount < 0 ||
                weaponAmmoCount > weaponAmmoCapacity)
            {
                Logger.Error(
                    "Something is completely wrong during reloading! Result ammo count is: " + weaponAmmoCount);
                weaponAmmoCount = 0;
            }

            itemWeaponPrivateState.AmmoCount = (ushort)weaponAmmoCount;

            Logger.Info(
                $"Weapon reloaded: {itemWeapon} - ammo {weaponAmmoCount}/{weaponAmmoCapacity} {selectedProtoItemAmmo?.ToString() ?? "<no ammo>"}",
                character);
        }