示例#1
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);
        }
        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 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);
        }
        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);
        }
        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);
        }
示例#6
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);
        }
示例#7
0
        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);
            }
        }
        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);
        }
        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;
        }
示例#10
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);
            }
        }
示例#11
0
        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);
        }
示例#12
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);
        }
示例#13
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);
        }