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); } }
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); }
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); } } }
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); }