Example #1
0
        protected override void ClientInteractStart(ClientObjectData data)
        {
            ClientSingleSimultaneousInteractionLimiter.InvokeForGameObject(
                data.GameObject,
                async() =>
            {
                var result = await this.CallServer(_ => _.ServerRemote_Pickup(data.GameObject));
                if (result is null)
                {
                    this.SoundPresetObject.PlaySound(ObjectSound.InteractFail);
                    return;
                }

                if (this.SoundPresetObject.HasSound(ObjectSound.InteractSuccess))
                {
                    this.SoundPresetObject.PlaySound(ObjectSound.InteractSuccess);
                }
                else
                {
                    ItemsSoundPresets.ItemGeneric.PlaySound(ItemSound.Pick,
                                                            pitch: RandomHelper.Range(0.95f, 1.05f));
                }

                NotificationSystem.ClientShowItemsNotification(result);
            });
        protected override void ClientInteractStart(ClientObjectData data)
        {
            if (ClientItemsManager.ItemInHand != null)
            {
                // in that case we want to allow place item from the hand
                // it will be handled automatically via ClientTryDropItemOnGround() call
                return;
            }

            var currentPlayerCharacter = Client.Characters.CurrentPlayerCharacter;
            var containerGround        = data.SyncPublicState.ItemsContainer;

            if (!Api.Client.Input.IsKeyHeld(InputKey.Control, evenIfHandled: true))
            {
                // try pickup all the items
                var result = currentPlayerCharacter.ProtoCharacter.ClientTryTakeAllItems(
                    currentPlayerCharacter,
                    containerGround,
                    showNotificationIfInventoryFull: false);
                if (result.MovedItems.Count > 0)
                {
                    // at least one item taken from ground
                    NotificationSystem.ClientShowItemsNotification(
                        itemsChangedCount: result.MovedItems
                        .GroupBy(p => p.Key.ProtoItem)
                        .ToDictionary(p => p.Key, p => p.Sum(v => v.Value)));
                }
            }

            if (containerGround.OccupiedSlotsCount > 0)
            {
                ClientOpenContainerExchangeUI(data.GameObject);
            }
        }
Example #3
0
 private void ClientRemote_ActionCompleted(Dictionary <IProtoItem, int> itemsChangedCount)
 {
     // play refill sound
     Client.Audio.PlayOneShot(new SoundResource("Items/Tools/WateringCan/Refill"));
     // display the removed empty bottle notification
     NotificationSystem.ClientShowItemsNotification(new Dictionary <IProtoItem, int>()
     {
         { ProtoItemBottleEmpty.Value, -1 }
     });
     // display the added water bottle notification
     NotificationSystem.ClientShowItemsNotification(itemsChangedCount);
 }
Example #4
0
        protected override void CheckInteractionQueue()
        {
            while (interactionQueue.Count != 0)
            {
                if (!interactionQueue[0].IsDestroyed &&
                    interactionQueue[0].ProtoStaticWorldObject
                    .SharedCanInteract(CurrentCharacter, interactionQueue[0], false))
                {
                    if (interactionQueue[0].ProtoWorldObject is ObjectGroundItemsContainer)
                    {
                        var containerGround = interactionQueue[0]
                                              .GetPublicState <ObjectGroundItemsContainer.PublicState>().ItemsContainer;

                        // try pickup all the items
                        var result = CurrentCharacter.ProtoCharacter.ClientTryTakeAllItems(
                            CurrentCharacter,
                            containerGround,
                            showNotificationIfInventoryFull: true);
                        if (result.MovedItems.Count > 0)
                        {
                            // at least one item taken from ground
                            NotificationSystem.ClientShowItemsNotification(
                                itemsChangedCount: result.MovedItems
                                .GroupBy(p => p.Key.ProtoItem)
                                .ToDictionary(p => p.Key, p => p.Sum(v => v.Value)));
                        }
                    }
                    else
                    {
                        interactionQueue[0].ProtoWorldObject.ClientInteractStart(interactionQueue[0]);
                        interactionQueue[0].ProtoWorldObject.ClientInteractFinish(interactionQueue[0]);
                    }
                }
                interactionQueue.RemoveAt(0);
            }
            // Known issue, cannot pickup ground container items while item in hands
            // \Scripts\StaticObjects\ObjectGroundItemsContainer.cs:409
        }
        public static async void ClientTryDropItemOnGround(
            IItem itemToDrop,
            ushort countToDrop,
            Vector2Ushort?dropTilePosition = null)
        {
            countToDrop = Math.Min(countToDrop, itemToDrop.Count);

            var character = Client.Characters.CurrentPlayerCharacter;

            if (!dropTilePosition.HasValue)
            {
                if (ClientTryDropItemToGroundContainerNearby(
                        character.Tile,
                        itemToDrop,
                        countToDrop,
                        out dropTilePosition,
                        out var resultItemsContainer))
                {
                    OnSuccess(resultItemsContainer);
                    return;
                }

                countToDrop = Math.Min(countToDrop, itemToDrop.Count);

                var obstaclesOnTheWay = false;
                if (!dropTilePosition.HasValue ||
                    !SharedIsWithinInteractionDistance(
                        character,
                        dropTilePosition.Value,
                        out obstaclesOnTheWay))
                {
                    NotificationSystem.ClientShowNotification(
                        obstaclesOnTheWay
                            ? CoreStrings.Notification_ObstaclesOnTheWay
                            : NotificationNoFreeSpaceToDrop,
                        color: NotificationColor.Bad,
                        icon: TextureResourceSack);
                    return;
                }
            }

            var tilePosition = dropTilePosition.Value;

            if (!SharedIsWithinInteractionDistance(
                    character,
                    tilePosition,
                    out var obstaclesOnTheWay2))
            {
                NotificationSystem.ClientShowNotification(
                    obstaclesOnTheWay2
                        ? CoreStrings.Notification_ObstaclesOnTheWay
                        : CoreStrings.Notification_TooFar,
                    NotificationCannotDropItemThere,
                    NotificationColor.Bad,
                    TextureResourceSack);
                return;
            }

            var tile = Client.World.GetTile(tilePosition);
            var objectGroundContainer = tile.StaticObjects.FirstOrDefault(_ => _.ProtoGameObject == instance);

            if (objectGroundContainer is null)
            {
                if (!instance.CheckTileRequirements(tilePosition, character, logErrors: false))
                {
                    // cannot drop item here
                    NotificationSystem.ClientShowNotification(
                        CoreStrings.Notification_ObstaclesOnTheWay,
                        NotificationCannotDropItemThere,
                        NotificationColor.Bad,
                        TextureResourceSack);
                    return;
                }

                Logger.Info(
                    $"Requested placing item on the ground (new ground container needed): {itemToDrop}. Count={countToDrop}.");
                objectGroundContainer = await instance.CallServer(
                    _ => _.ServerRemote_DropItemOnGround(
                        itemToDrop,
                        countToDrop,
                        tilePosition));

                if (objectGroundContainer != null)
                {
                    // successfully placed on ground
                    OnSuccess(GetPublicState(objectGroundContainer).ItemsContainer);
                    return;
                }

                // we're continue the async call - the context might have been changed
                if (itemToDrop.IsDestroyed)
                {
                    return;
                }

                // was unable to place the item on the ground - maybe it was already placed with an earlier call
                if (itemToDrop.Container?.OwnerAsStaticObject?.ProtoStaticWorldObject is ObjectGroundItemsContainer)
                {
                    // it seems to be on the ground now
                    return;
                }

                // the action is definitely failed
                instance.SoundPresetObject.PlaySound(ObjectSound.InteractFail);
                return;
            }

            if (!instance.SharedCanInteract(character, objectGroundContainer, writeToLog: true))
            {
                return;
            }

            // get items container instance
            var groundItemsContainer = GetPublicState(objectGroundContainer).ItemsContainer;

            // try move item to the ground items container
            if (!Client.Items.MoveOrSwapItem(
                    itemToDrop,
                    groundItemsContainer,
                    countToMove: countToDrop,
                    isLogErrors: false))
            {
                // cannot move - open container UI
                ClientOpenContainerExchangeUI(objectGroundContainer);
                return;
            }

            // item moved successfully
            OnSuccess(groundItemsContainer);

            void OnSuccess(IItemsContainer resultGroundItemsContainer)
            {
                itemToDrop.ProtoItem.ClientOnItemDrop(itemToDrop, resultGroundItemsContainer);

                NotificationSystem.ClientShowItemsNotification(
                    itemsChangedCount: new Dictionary <IProtoItem, int>()
                {
                    { itemToDrop.ProtoItem, -countToDrop }
                });

                if (Api.Client.Input.IsKeyHeld(InputKey.Shift, evenIfHandled: true))
                {
                    // open container UI to allow faster items exchange with it
                    ClientOpenContainerExchangeUI(resultGroundItemsContainer.OwnerAsStaticObject);
                }
            }
        }
Example #6
0
        protected override void CheckInteractionQueue()
        {
            if (openedLootContainer != null)
            {
                if (InteractionCheckerSystem.SharedHasInteraction(CurrentCharacter, openedLootContainer, true))
                {
                    // We get container private state, now take all items from container.
                    var q      = openedLootContainer.GetPrivateState <LootContainerPrivateState>();
                    var result =
                        CurrentCharacter.ProtoCharacter.ClientTryTakeAllItems(CurrentCharacter, q.ItemsContainer, true);
                    if (result.MovedItems.Count > 0)
                    {
                        NotificationSystem.ClientShowItemsNotification(
                            itemsChangedCount: result.MovedItems
                            .GroupBy(p => p.Key.ProtoItem)
                            .ToDictionary(p => p.Key, p => p.Sum(v => v.Value)));
                    }
                    InteractionCheckerSystem.CancelCurrentInteraction(CurrentCharacter);
                }
                else if (openedLootContainer.ProtoWorldObject
                         .SharedCanInteract(CurrentCharacter, openedLootContainer, false))
                {
                    // Waiting for container private state from server.
                    return;
                }
                openedLootContainer = null;
                readyForInteraction = true;
            }

            if (!readyForInteraction)
            {
                return;
            }

            // Remove from queue while it have object and they in our whitelist if:
            //  - object is destroyed
            //  - if object is container that we already have looted
            //  - if object not IProtoObjectGatherable
            //  - if we can not interact with object right now
            //  - if we can not gather anything from object
            while (interactionQueue.Count != 0 && EnabledEntityList.Contains(interactionQueue[0].ProtoGameObject) &&
                   (interactionQueue[0].IsDestroyed ||
                    (lastActionState != null &&
                     lastActionState.TargetWorldObject == interactionQueue[0] &&
                     lastActionState.IsCompleted &&
                     !lastActionState.IsCancelled &&
                     !lastActionState.IsCancelledByServer) ||
                    !(interactionQueue[0].ProtoGameObject is IProtoObjectGatherable protoGatherable) ||
                    !protoGatherable.SharedCanInteract(CurrentCharacter, interactionQueue[0], false) ||
                    !protoGatherable.SharedIsCanGather(interactionQueue[0])))
            {
                interactionQueue.RemoveAt(0);
            }

            if (interactionQueue.Count == 0)
            {
                return;
            }

            var request = new WorldActionRequest(CurrentCharacter, interactionQueue[0]);

            GatheringSystem.Instance.SharedStartAction(request);
        }
        private static int SharedTryConsumeWaterBottles(
            WateringCanRefillActionState state,
            ICharacter character,
            int waterAmount,
            int waterCapacity)
        {
            IItemsServerService serverItemsService = null;
            IItemsClientService clientItemsService = null;

            var isServer = IsServer;

            if (isServer)
            {
                serverItemsService = Server.Items;
            }
            else
            {
                clientItemsService = Client.Items;
            }

            var totalUsedBottlesCount = 0;
            var maxBottlesToConsume   = MaxBottlesToConsumePerRefill;

            foreach (var itemBottle in state.ItemsToConsumeForRefill)
            {
                // check if character owns this item
                if (!character.ProtoCharacter
                    .SharedEnumerateAllContainers(character, includeEquipmentContainer: false)
                    .Any(c => c.Items.Contains(itemBottle)))
                {
                    throw new Exception("The character doesn't own " + itemBottle + " - cannot use it to reload");
                }

                int itemBottleCountToSubstract;

                var itemBottleCount = itemBottle.Count;
                if (itemBottleCount == 0)
                {
                    continue;
                }

                if (itemBottleCount > maxBottlesToConsume)
                {
                    itemBottleCount = (ushort)maxBottlesToConsume;
                }

                if (waterAmount + itemBottleCount * BottleWaterAmount
                    >= waterCapacity)
                {
                    // there are more than enough item count in that item stack to fully refill the watering can
                    itemBottleCountToSubstract =
                        (int)Math.Ceiling((waterCapacity - waterAmount) / (double)BottleWaterAmount);
                    waterAmount = waterCapacity;
                }
                else
                {
                    // consume full item stack
                    itemBottleCountToSubstract = itemBottleCount;
                    waterAmount += itemBottleCount * BottleWaterAmount;
                }

                if (itemBottleCountToSubstract > 0)
                {
                    maxBottlesToConsume   -= itemBottleCountToSubstract;
                    totalUsedBottlesCount += itemBottleCountToSubstract;

                    // reduce item count
                    var itemBottleNewCount = itemBottle.Count - itemBottleCountToSubstract;
                    if (isServer)
                    {
                        serverItemsService.SetCount(
                            itemBottle,
                            itemBottleNewCount,
                            byCharacter: character,
                            // reloading is also processed on Client-side separately, so no need to send updates
                            isSendingUpdatesToPlayer: false);

                        // spawn empty bottles
                        ItemBottleEmpty.ServerSpawnEmptyBottle(character, (ushort)itemBottleCountToSubstract);
                    }
                    else // if (IsClient)
                    {
                        clientItemsService.SetCount(itemBottle, itemBottleNewCount);
                    }
                }

                if (waterAmount >= waterCapacity)
                {
                    // fully refilled
                    break;
                }

                if (maxBottlesToConsume <= 0)
                {
                    // amount of bottles to consume exceeded
                    break;
                }
            }

            if (IsClient)
            {
                NotificationSystem.ClientShowItemsNotification(
                    new Dictionary <IProtoItem, int>()
                {
                    { GetProtoEntity <ItemBottleWater>(), -totalUsedBottlesCount }
                });
            }

            return(waterAmount);
        }