Esempio n. 1
0
        public bool ClientCanStartRefill(IItem item)
        {
            var publicState = GetPublicState(item);

            if (!publicState.IsActive)
            {
                return(true);
            }

            // light is active - try deactivate it
            this.ClientTrySetActiveState(item, setIsActive: false);
            if (!publicState.IsActive)
            {
                return(true);
            }

            // still active!
            NotificationSystem.ClientShowNotification(
                NotificationCannotRefillWhileOn_Title,
                NotificationCannotRefillWhileOn_Message,
                color: NotificationColor.Bad,
                icon: this.Icon);
            return(false);
        }
        public override bool CanRemoveItem(CanRemoveItemContext context)
        {
            if (!(context.Item.ProtoItem is IProtoItemEquipmentImplant protoItemEquipment))
            {
                // impossible - how did it end up here?
                return(true);
            }

            var itemEquipmentType = protoItemEquipment.EquipmentType;

            if (itemEquipmentType == EquipmentType.Implant)
            {
                // implant item
                if (context.ByCharacter == null ||
                    CreativeModeSystem.SharedIsInCreativeMode(context.ByCharacter))
                {
                    // Allowed to add/remove implant item by the game only (via medical station).
                    // But allow to characters in the creative mode to do this directly.
                    return(true);
                }

                if (IsClient)
                {
                    NotificationSystem.ClientShowNotification(
                        NotificationUseStationToRemoveImplant_Title,
                        NotificationUseStationToRemoveImplant_Message,
                        NotificationColor.Bad,
                        protoItemEquipment.Icon);
                }

                return(false);
            }

            // can remove anything
            return(true);
        }
Esempio n. 3
0
        private void ClientApplyWorldSizeSliceExpansion()
        {
            var location     = this.settingsViewModel.ViewModelSelectWorldSizeSliceExpansionLocation;
            var insertedArea = new BoundsUshort(new Vector2Ushort(location.OffsetX, location.OffsetY),
                                                new Vector2Ushort(location.SizeX, location.SizeY));

            if (insertedArea.Size == Vector2Ushort.Zero)
            {
                NotificationSystem.ClientShowNotification("Please enter the size of expansion",
                                                          color: NotificationColor.Bad);
                return;
            }

            if (insertedArea.Size.X % ScriptingConstants.WorldChunkSize != 0 ||
                insertedArea.Size.Y % ScriptingConstants.WorldChunkSize != 0)
            {
                NotificationSystem.ClientShowNotification(
                    $"Please ensure that the size of expansion can be divided on the world chunk size ({ScriptingConstants.WorldChunkSize}) without the remainder",
                    color: NotificationColor.Bad);
                return;
            }

            this.CallServer(_ => _.ServerRemote_ApplyWorldSizeSliceExpansion(insertedArea));
        }
Esempio n. 4
0
        public static void ClientTryReloadOrSwitchAmmoType(
            bool isSwitchAmmoType,
            bool sendToServer             = true,
            bool?showNotificationIfNoAmmo = null)
        {
            var character          = Api.Client.Characters.CurrentPlayerCharacter;
            var currentWeaponState = PlayerCharacter.GetPrivateState(character).WeaponState;

            var itemWeapon = currentWeaponState.ItemWeapon;

            if (itemWeapon is null)
            {
                // no active weapon to reload
                return;
            }

            var protoWeapon = (IProtoItemWeapon)itemWeapon.ProtoItem;

            if (protoWeapon.AmmoCapacity == 0)
            {
                // the item is non-reloadable
                return;
            }

            var itemPrivateState = itemWeapon.GetPrivateState <WeaponPrivateState>();
            var ammoCountNeed    = isSwitchAmmoType
                                    ? protoWeapon.AmmoCapacity
                                    : (ushort)Math.Max(0, protoWeapon.AmmoCapacity - itemPrivateState.AmmoCount);

            if (ammoCountNeed == 0)
            {
                Logger.Info("No need to reload the weapon " + itemWeapon, character);
                return;
            }

            var compatibleAmmoGroups = SharedGetCompatibleAmmoGroups(character, protoWeapon);

            if (compatibleAmmoGroups.Count == 0 &&
                !isSwitchAmmoType)
            {
                if (showNotificationIfNoAmmo.HasValue && showNotificationIfNoAmmo.Value ||
                    currentWeaponState.SharedGetInputIsFiring())
                {
                    protoWeapon.SoundPresetWeapon.PlaySound(WeaponSound.Empty,
                                                            character,
                                                            volume: SoundConstants.VolumeWeapon);
                    NotificationSystem.ClientShowNotification(
                        NotificationNoAmmo_Title,
                        NotificationNoAmmo_Message,
                        NotificationColor.Bad,
                        protoWeapon.Icon,
                        playSound: false);
                }

                if (currentWeaponState.SharedGetInputIsFiring())
                {
                    // stop firing the weapon
                    currentWeaponState.ProtoWeapon.ClientItemUseFinish(itemWeapon);
                }

                return;
            }

            IProtoItemAmmo selectedProtoItemAmmo = null;

            var currentReloadingState = currentWeaponState.WeaponReloadingState;

            if (currentReloadingState is null)
            {
                // don't have reloading state - find ammo item matching current weapon ammo type
                var currentProtoItemAmmo = itemPrivateState.CurrentProtoItemAmmo;
                if (currentProtoItemAmmo is null)
                {
                    // no ammo selected in weapon
                    selectedProtoItemAmmo = SharedFindNextAmmoGroup(protoWeapon.CompatibleAmmoProtos,
                                                                    compatibleAmmoGroups,
                                                                    currentProtoItemAmmo: null)?.Key;
                }
                else // if weapon already has ammo
                {
                    if (isSwitchAmmoType)
                    {
                        selectedProtoItemAmmo = SharedFindNextAmmoGroup(protoWeapon.CompatibleAmmoProtos,
                                                                        compatibleAmmoGroups,
                                                                        currentProtoItemAmmo)?.Key;
                        if (selectedProtoItemAmmo == currentProtoItemAmmo &&
                            itemPrivateState.AmmoCount >= protoWeapon.AmmoCapacity)
                        {
                            // this ammo type is already loaded and it's fully reloaded
                            Logger.Info("No need to reload the weapon " + itemWeapon, character);
                            return;
                        }
                    }
                    else // simple reload requested
                    {
                        // try to find ammo of the same type as already loaded into the weapon
                        var isFound = false;
                        foreach (var ammoGroup in compatibleAmmoGroups)
                        {
                            if (ammoGroup.Key == currentProtoItemAmmo)
                            {
                                isFound = true;
                                selectedProtoItemAmmo = currentProtoItemAmmo;
                                break;
                            }
                        }

                        if (!isFound)
                        {
                            // no group selected - select first
                            isSwitchAmmoType      = true;
                            sendToServer          = true;
                            selectedProtoItemAmmo = SharedFindNextAmmoGroup(protoWeapon.CompatibleAmmoProtos,
                                                                            compatibleAmmoGroups,
                                                                            currentProtoItemAmmo: null)?.Key;
                        }
                    }
                }
            }
            else
            {
                if (!isSwitchAmmoType)
                {
                    // already reloading
                    return;
                }

                // already reloading - try select another ammo type (alternate between them)
                var currentReloadingProtoItemAmmo = currentReloadingState.ProtoItemAmmo;
                selectedProtoItemAmmo = SharedFindNextAmmoGroup(protoWeapon.CompatibleAmmoProtos,
                                                                compatibleAmmoGroups,
                                                                currentReloadingProtoItemAmmo)?.Key;

                if (selectedProtoItemAmmo == currentReloadingProtoItemAmmo)
                {
                    // already reloading this ammo type
                    return;
                }
            }

            if (currentReloadingState != null &&
                currentReloadingState.ProtoItemAmmo == selectedProtoItemAmmo)
            {
                // already reloading with these ammo items
                return;
            }

            if (currentReloadingState is null &&
                selectedProtoItemAmmo is null &&
                itemPrivateState.CurrentProtoItemAmmo is null)
            {
                // already unloaded
                return;
            }

            // create reloading state on the Client-side
            var weaponReloadingState = new WeaponReloadingState(
                character,
                itemWeapon,
                protoWeapon,
                selectedProtoItemAmmo);

            currentWeaponState.WeaponReloadingState = weaponReloadingState;

            protoWeapon.SoundPresetWeapon.PlaySound(WeaponSound.Reload,
                                                    character,
                                                    SoundConstants.VolumeWeapon);
            Logger.Info(
                $"Weapon reloading started for {itemWeapon} reload duration: {weaponReloadingState.SecondsToReloadRemains:F2}s",
                character);

            if (weaponReloadingState.SecondsToReloadRemains <= 0)
            {
                // instant-reload weapon - perform local reloading
                SharedProcessWeaponReload(character, currentWeaponState, out _);
            }

            if (sendToServer || isSwitchAmmoType)
            {
                // perform reload on server
                var arg = new ReloadWeaponRequest(itemWeapon, selectedProtoItemAmmo);
                Instance.CallServer(_ => _.ServerRemote_ReloadWeapon(arg));
            }
        }
Esempio n. 5
0
        public static bool SharedCheckCanInteract(
            ICharacter character,
            IDynamicWorldObject vehicle,
            bool writeToLog)
        {
            if (vehicle is null ||
                vehicle.IsDestroyed)
            {
                return(false);
            }

            // it's possible to repair any vehicle within a certain distance to the character
            var canInteract = character.Position.DistanceSquaredTo(vehicle.Position)
                              <= MaxDistanceForRepairAction * MaxDistanceForRepairAction;

            if (!canInteract)
            {
                if (writeToLog)
                {
                    Logger.Warning(
                        $"Character cannot interact with {vehicle} for repair - too far",
                        character);

                    if (IsClient)
                    {
                        CannotInteractMessageDisplay.ClientOnCannotInteract(vehicle,
                                                                            CoreStrings.Notification_TooFar,
                                                                            isOutOfRange: true);
                    }
                }

                return(false);
            }

            var physicsSpace    = character.PhysicsBody.PhysicsSpace;
            var characterCenter = character.Position + character.PhysicsBody.CenterOffset;

            if (ObstacleTestHelper.SharedHasObstaclesInTheWay(characterCenter,
                                                              physicsSpace,
                                                              vehicle,
                                                              sendDebugEvents: writeToLog))
            {
                if (writeToLog)
                {
                    Logger.Warning(
                        $"Character cannot interact with {vehicle} for repair - obstacles in the way",
                        character);

                    if (IsClient)
                    {
                        CannotInteractMessageDisplay.ClientOnCannotInteract(vehicle,
                                                                            CoreStrings.Notification_ObstaclesOnTheWay,
                                                                            isOutOfRange: true);
                    }
                }

                return(false);
            }

            using var tempCharactersNearby = Api.Shared.GetTempList <ICharacter>();
            if (IsClient)
            {
                Client.Characters.GetKnownPlayerCharacters(tempCharactersNearby);
            }
            else
            {
                Server.World.GetScopedByPlayers(vehicle, tempCharactersNearby);
            }

            foreach (var otherPlayerCharacter in tempCharactersNearby.AsList())
            {
                if (ReferenceEquals(character, otherPlayerCharacter))
                {
                    continue;
                }

                if (PlayerCharacter.GetPublicState(otherPlayerCharacter).CurrentPublicActionState
                    is VehicleRepairActionState.PublicState repairActionState &&
                    ReferenceEquals(repairActionState.TargetWorldObject, vehicle))
                {
                    // already repairing by another player
                    if (!writeToLog)
                    {
                        return(false);
                    }

                    Logger.Important($"Cannot start repairing {vehicle} - already repairing by another player",
                                     character);
                    if (IsClient)
                    {
                        NotificationSystem.ClientShowNotification(
                            CoreStrings.Notification_ErrorCannotInteract,
                            CoreStrings.Notification_ErrorObjectUsedByAnotherPlayer,
                            NotificationColor.Bad,
                            icon: ((IProtoVehicle)vehicle.ProtoGameObject).Icon);
                    }

                    return(false);
                }
            }

            return(true);
        }
        private static void InvitationsCollectionChangedHandler(object sender, NotifyCollectionChangedEventArgs e)
        {
            string inviterName;

            switch (e.Action)
            {
            case NotifyCollectionChangedAction.Add:
            {
                inviterName = (string)e.NewItems[0];
                ShowNotification(inviterName);
                break;
            }

            case NotifyCollectionChangedAction.Remove:
            {
                inviterName = (string)e.OldItems[0];
                if (NotificationsFromInviteeDictionary.TryGetValue(inviterName, out var weakReference) &&
                    weakReference.TryGetTarget(out var control))
                {
                    control.Hide(quick: true);
                }

                NotificationsFromInviteeDictionary.Remove(inviterName);
                break;
            }

            case NotifyCollectionChangedAction.Reset:
            {
                // hide all current notifications
                foreach (var notificationControl in NotificationsFromInviteeDictionary)
                {
                    var weakReference = notificationControl.Value;
                    if (weakReference.TryGetTarget(out var control))
                    {
                        control.Hide(quick: true);
                    }
                }

                NotificationsFromInviteeDictionary.Clear();

                // display new notifications
                foreach (var name in PartySystem.ClientCurrentInvitationsFromCharacters)
                {
                    ShowNotification(name);
                }

                break;
            }

            default:
                throw new ArgumentOutOfRangeException();
            }

            void ShowNotification(string name)
            {
                if (ClientChatBlockList.IsBlocked(name))
                {
                    // don't display invitations from blocked players
                    return;
                }

                var control = NotificationSystem.ClientShowNotification(
                    title: PartyInvitationTitle,
                    message: string.Format(InvitationMessageFormat, name),
                    onClick: () => ShowInvitationDialog(name),
                    autoHide: false,
                    icon: IconPartyInvitation);

                control.CallbackOnRightClickHide = () => PartySystem.ClientInvitationDecline(name);

                NotificationsFromInviteeDictionary.Add(
                    name,
                    new WeakReference <HudNotificationControl>(control));
            }
        }
Esempio n. 7
0
        public override bool CanAddItem(CanAddItemContext context)
        {
            if (context.Item.ProtoItem
                is not IProtoItemEquipment protoItemEquipment)
            {
                // not an equipment - cannot be placed here
                return(false);
            }

            if (!context.SlotId.HasValue)
            {
                // no specific slot provided - so answer "true" because any equipment item could be added here
                return(true);
            }

            var slotIdValue       = (EquipmentType)context.SlotId.Value;
            var itemEquipmentType = protoItemEquipment.EquipmentType;

            if (!context.IsExploratoryCheck &&
                (itemEquipmentType == EquipmentType.Armor ||
                 itemEquipmentType == EquipmentType.FullBody) &&
                !StatusEffectPeredozinApplication.SharedCheckCanEquipArmor(context.Container.OwnerAsCharacter,
                                                                           clientShowNotification: true))
            {
                // don't allow equipping armor while peredozin is applying
                return(false);
            }

            if (itemEquipmentType == EquipmentType.FullBody)
            {
                if (slotIdValue != EquipmentType.Armor)
                {
                    // cannot place full body armor in another slots
                    return(false);
                }

                if (context.IsExploratoryCheck)
                {
                    // no more checks
                    return(true);
                }

                // It's actual operation, perform more checks!
                // Need to verify that head is an empty slot
                // Please note: can equip full body when there is an armor without the head item - will swap items.
                var container = context.Container;
                if (IsSlotEmpty(container, EquipmentType.Head))
                {
                    // can place here
                    return(true);
                }

                // head and legs are not empty
                if (IsClient)
                {
                    NotificationSystem.ClientShowNotification(
                        NotificationCannotEquip,
                        NotificationRemoveOtherEquipment,
                        NotificationColor.Bad,
                        protoItemEquipment.Icon);
                }

                return(false);
            }

            var isValidSlot = protoItemEquipment.CompatibleContainerSlotsIds.Contains((byte)slotIdValue);

            if (!isValidSlot)
            {
                // the selected slot doesn't match the equipment
                return(false);
            }

            if (itemEquipmentType == EquipmentType.Implant)
            {
                // implant item
                if (context.ByCharacter is null ||
                    CreativeModeSystem.SharedIsInCreativeMode(context.ByCharacter))
                {
                    // Allowed to add/remove implant item by the game only (via medical station).
                    // But allow to characters in the creative mode to do this directly.
                    return(true);
                }

                if (IsClient &&
                    !context.IsExploratoryCheck &&
                    protoItemEquipment is not ItemImplantBroken)
                {
                    NotificationSystem.ClientShowNotification(
                        NotificationUseStationToInstallImplant_Title,
                        NotificationUseStationToInstallImplant_Message,
                        NotificationColor.Bad,
                        protoItemEquipment.Icon);
                }

                return(false);
            }

            if (!context.IsExploratoryCheck &&
                itemEquipmentType == EquipmentType.Head)
            {
                // Regular equipment - can equip head ONLY if there is no full body armor.
                // Please note: can equip regular armor (without the helmet) even if there is full body armor - will swap items.
                if (IsHasFullBodyArmor(context.Container))
                {
                    // has full body armor - cannot equip this item
                    if (IsClient)
                    {
                        NotificationSystem.ClientShowNotification(
                            NotificationCannotEquip,
                            NotificationRemoveFullBody,
                            NotificationColor.Bad,
                            protoItemEquipment.Icon);
                    }

                    return(false);
                }
            }

            return(true);
        }
Esempio n. 8
0
        private static void SharedStartAction(ICharacter character, IWorldObject worldObject)
        {
            if (worldObject == null)
            {
                return;
            }

            if (!SharedCheckCanInteract(character, worldObject, writeToLog: true))
            {
                return;
            }

            if (!(worldObject.ProtoGameObject is IProtoObjectStructure))
            {
                throw new Exception("Not a structure: " + worldObject);
            }

            var characterPrivateState = PlayerCharacter.GetPrivateState(character);
            var characterPublicState  = PlayerCharacter.GetPublicState(character);

            if (characterPrivateState.CurrentActionState is DeconstructionActionState actionState &&
                actionState.WorldObject == worldObject)
            {
                // already deconstructing
                return;
            }

            var selectedHotbarItem = characterPublicState.SelectedHotbarItem;

            if (!(selectedHotbarItem?.ProtoGameObject is IProtoItemToolCrowbar))
            {
                selectedHotbarItem = null;
                if (!(worldObject.ProtoWorldObject is ProtoObjectConstructionSite))
                {
                    // no crowbar tool is selected, only construction sites can be deconstructed without the crowbar
                    return;
                }
            }

            actionState = new DeconstructionActionState(character, (IStaticWorldObject)worldObject, selectedHotbarItem);
            if (!actionState.CheckIsAllowed())
            {
                // not allowed to deconstruct
                Logger.Warning(
                    $"Deconstruction is not allowed: {worldObject} by {character}",
                    character);

                if (Api.IsClient)
                {
                    NotificationSystem.ClientShowNotification(
                        NotificationNotLandOwner_Title,
                        NotificationNotLandOwner_Message,
                        NotificationColor.Bad,
                        selectedHotbarItem?.ProtoItem.Icon);
                }

                return;
            }

            if (!actionState.CheckIsNeeded())
            {
                // action is not needed
                Logger.Important($"Deconstruction is not required: {worldObject} by {character}", character);
                return;
            }

            characterPrivateState.SetCurrentActionState(actionState);

            Logger.Important($"Deconstruction started: {worldObject} by {character}", character);

            if (IsClient)
            {
                // TODO: display crowbar started animation? Send animation to other players?
                Instance.CallServer(_ => _.ServerRemote_StartAction(worldObject));
            }
        }
Esempio n. 9
0
 private void ClientRemote_UnstuckAlreadyQueued()
 {
     NotificationSystem.ClientShowNotification(
         NotificationAlreadyRequestedUnstuck_Title,
         NotificationAlreadyRequestedUnstuck_Message);
 }
Esempio n. 10
0
        public static void ClientPaste(Vector2Ushort tilePosition)
        {
            if (lastBufferEntry is null)
            {
                return;
            }

            var bufferEntry = lastBufferEntry.Value;

            if (ClientEditorAreaSelectorHelper.Instance is not null)
            {
                NotificationSystem.ClientShowNotification(
                    title: null,
                    message:
                    "You're already in object placement mode."
                    + ObjectPlacementGuide,
                    color: NotificationColor.Neutral);
                return;
            }

            NotificationSystem.ClientShowNotification(
                title: null,
                message:
                $"{bufferEntry.SpawnList.Count} objects ready for paste!"
                + ObjectPlacementGuide,
                color: NotificationColor.Good);

            var originalTileStartX = bufferEntry.TilePositions.Min(t => t.X);
            var originalTileStartY = bufferEntry.TilePositions.Min(t => t.Y);
            var originalTileEndX   = bufferEntry.TilePositions.Max(t => t.X);
            var originalTileEndY   = bufferEntry.TilePositions.Max(t => t.Y);

            var originalSize = new Vector2Ushort((ushort)(originalTileEndX - originalTileStartX + 1),
                                                 (ushort)(originalTileEndY - originalTileStartY + 1));

            // ReSharper disable once ObjectCreationAsStatement
            new ClientEditorAreaSelectorHelper(tilePosition,
                                               originalSize,
                                               selectedCallback: PlaceSelectedCallback);

            void PlaceSelectedCallback(Vector2Ushort selectedTilePosition)
            {
                var originalStartX = bufferEntry.SpawnList.Min(t => t.TilePosition.X);
                var originalStartY = bufferEntry.SpawnList.Min(t => t.TilePosition.Y);
                var offset         = selectedTilePosition.ToVector2Int() - (originalStartX, originalStartY);

                var storageEntry = new ActionStorageEntry(
                    spawnBatches: bufferEntry.SpawnList
                    .Select(t => new SpawnObjectRequest(
                                t.Prototype,
                                t.TilePosition + offset))
                    .Batch(20000)
                    .Select(b => b.ToList())
                    .ToList());

                EditorClientActionsHistorySystem.DoAction(
                    "Paste objects",
                    onDo: async() =>
                {
                    var spawnBatches = storageEntry.SpawnBatches;
                    for (var index = 0; index < spawnBatches.Count; index++)
                    {
                        var batch             = spawnBatches[index];
                        var spawnedObjectsIds =
                            await WorldObjectsEditingSystem.Instance.CallServer(
                                _ => _.ServerRemote_SpawnObjects(batch));
                        // record spawned objects IDs so they could be removed on undo
                        var undoSpawnBatch = storageEntry.UndoSpawnBatches[index];
                        undoSpawnBatch.Clear();
                        undoSpawnBatch.AddRange(spawnedObjectsIds);
                    }

                    NotificationSystem.ClientShowNotification(
                        title: null,
                        message: spawnBatches.Sum(r => r.Count) + " object(s) pasted!",
                        color: NotificationColor.Good);
                },
                    onUndo: () =>
                {
                    var countRemoved = 0;
                    foreach (var batch in storageEntry.UndoSpawnBatches)
                    {
                        countRemoved += batch.Count;
                        WorldObjectsEditingSystem.Instance.CallServer(
                            _ => _.ServerRemote_DeleteObjects(batch));
                        // the delete batch is no longer valid
                        batch.Clear();
                    }

                    NotificationSystem.ClientShowNotification(
                        title: null,
                        message: countRemoved + " objects(s) removed.",
                        color: NotificationColor.Neutral);
                },
                    canGroupWithPreviousAction: false);
            }
        }
Esempio n. 11
0
        public static bool SharedCheckCanInteract(
            ICharacter character,
            IWorldObject worldObject,
            bool writeToLog)
        {
            if (worldObject is null ||
                worldObject.IsDestroyed)
            {
                return(false);
            }

            var staticWorldObject = (IStaticWorldObject)worldObject;

            if (!staticWorldObject.ProtoStaticWorldObject.SharedCanInteract(character,
                                                                            worldObject,
                                                                            writeToLog))
            {
                return(false);
            }

            using var tempCharactersNearby = Api.Shared.GetTempList <ICharacter>();
            if (IsClient)
            {
                Client.Characters.GetKnownPlayerCharacters(tempCharactersNearby);
            }
            else
            {
                Server.World.GetScopedByPlayers(staticWorldObject, tempCharactersNearby);
            }

            foreach (var otherPlayerCharacter in tempCharactersNearby.AsList())
            {
                if (ReferenceEquals(character, otherPlayerCharacter))
                {
                    continue;
                }

                if (PlayerCharacter.GetPublicState(otherPlayerCharacter).CurrentPublicActionState
                    is HackingActionState.PublicState hackingState &&
                    ReferenceEquals(hackingState.TargetWorldObject, staticWorldObject))
                {
                    // already hacking by another player
                    if (!writeToLog)
                    {
                        return(false);
                    }

                    Logger.Important($"Cannot start hacking {staticWorldObject} - already hacking by another player",
                                     character);
                    if (IsClient)
                    {
                        NotificationSystem.ClientShowNotification(
                            CoreStrings.Notification_ErrorCannotInteract,
                            CoreStrings.Notification_ErrorObjectUsedByAnotherPlayer,
                            NotificationColor.Bad,
                            icon: staticWorldObject.ProtoStaticWorldObject.Icon);
                    }

                    return(false);
                }
            }

            return(true);
        }
        public static void ClientActivateShield(ILogicObject areasGroup)
        {
            var status = SharedGetShieldPublicStatus(areasGroup);

            if (status != ShieldProtectionStatus.Inactive)
            {
                Logger.Warning("The shield is already active or activating");
                return;
            }

            if (LandClaimSystem.SharedIsAreasGroupUnderRaid(areasGroup))
            {
                NotificationSystem.ClientShowNotification(CoreStrings.ShieldProtection_CannotActivateDuringRaidBlock,
                                                          color: NotificationColor.Bad);
                return;
            }

            SharedGetShieldProtectionMaxStatsForBase(
                areasGroup,
                out var maxShieldDuration,
                out var electricityCapacity);

            var privateState       = LandClaimAreasGroup.GetPrivateState(areasGroup);
            var electricityAmount  = privateState.ShieldProtectionCurrentChargeElectricity;
            var durationEstimation = maxShieldDuration * electricityAmount / electricityCapacity;

            var message = string.Format(
                CoreStrings.ShieldProtection_ActivationConfirmation_ProtectionDuration_Format,
                ClientTimeFormatHelper.FormatTimeDuration(
                    durationEstimation,
                    appendSeconds: false));

            var cooldownRemains = SharedCalculateCooldownRemains(areasGroup);

            message += "[br][br]";
            if (cooldownRemains <= 0)
            {
                message += string.Format(CoreStrings.ShieldProtection_ActivationConfirmation_DelayDuration_Format,
                                         ClientTimeFormatHelper.FormatTimeDuration(
                                             SharedActivationDuration,
                                             appendSeconds: true));
            }
            else
            {
                message += string.Format(
                    CoreStrings.ShieldProtection_ActivationConfirmation_DelayDurationWithCooldown_Format,
                    ClientTimeFormatHelper.FormatTimeDuration(
                        SharedActivationDuration + cooldownRemains,
                        appendSeconds: true));
            }

            message += "[br][br]";
            message += string.Format(CoreStrings.ShieldProtection_DeactivationNotes_Format,
                                     ClientTimeFormatHelper.FormatTimeDuration(
                                         SharedCooldownDuration,
                                         appendSeconds: false));

            message += "[br][br]";
            message += CoreStrings.ShieldProtection_Description_9;

            DialogWindow.ShowDialog(
                CoreStrings.ShieldProtection_Dialog_ConfirmActivation,
                message,
                okText: CoreStrings.ShieldProtection_Button_ActivateShield,
                okAction: () => Instance.CallServer(_ => _.ServerRemote_ActivateShield(areasGroup)),
                cancelAction: () => { },
                focusOnCancelButton: true);
        }
 private void ClientRemote_UnstuckImpossible()
 {
     NotificationSystem.ClientShowNotification(
         NotificationUnstuckImpossible_Title,
         color: NotificationColor.Bad);
 }
Esempio n. 14
0
        public ItemFuelRefillRequest ClientTryCreateRequest(ICharacter character, IItem item)
        {
            if (!(item?.ProtoGameObject is IProtoItemWithFuel protoItemFuelRefillable))
            {
                // no item selected
                return(null);
            }

            if (protoItemFuelRefillable.ItemFuelConfig.FuelProtoItemsList.Count == 0)
            {
                if (IsServer)
                {
                    Logger.Warning("Cannot refill - not refillable: " + item, character);
                }
                else // if (IsClient)
                {
                    NotificationSystem.ClientShowNotification(
                        NotificationNotRefillable,
                        color: NotificationColor.Bad,
                        icon: protoItemFuelRefillable.Icon);
                }

                return(null);
            }

            if (!SharedIsRefuelNeeded(item))
            {
                if (IsClient)
                {
                    NotificationSystem.ClientShowNotification(
                        NotificationNoNeedToRefill,
                        color: NotificationColor.Neutral,
                        icon: protoItemFuelRefillable.Icon);
                }

                return(null);
            }

            if (!protoItemFuelRefillable.ClientCanStartRefill(item))
            {
                return(null);
            }

            var fuelItemsToConsume = ClientSelectItemsToConsume(protoItemFuelRefillable,
                                                                amountNeeded: SharedGetFuelAmountNeeded(item),
                                                                MaxItemsToConsumePerRefill);

            if (fuelItemsToConsume.Count > 0)
            {
                return(new ItemFuelRefillRequest(character, item, fuelItemsToConsume));
            }

            NotificationSystem.ClientShowNotification(
                NotificationNeedFuel_Title,
                message: string.Format(NotificationNeedFuel_MessageFormat,
                                       protoItemFuelRefillable.ItemFuelConfig.FuelProtoItemsList.Select(i => i.Name)
                                       .GetJoinedString(", ")),
                color: NotificationColor.Bad,
                icon: protoItemFuelRefillable.Icon);
            return(null);
        }
Esempio n. 15
0
        private static void SharedStartAction(ICharacter character, IWorldObject worldObject)
        {
            if (!(worldObject?.ProtoGameObject is IProtoObjectStructure))
            {
                return;
            }

            var characterPrivateState = PlayerCharacter.GetPrivateState(character);
            var characterPublicState  = PlayerCharacter.GetPublicState(character);

            if (characterPrivateState.CurrentActionState is ConstructionActionState actionState &&
                actionState.WorldObject == worldObject)
            {
                // already building/repairing specified object
                return;
            }

            var selectedHotbarItem = characterPublicState.SelectedHotbarItem;

            if (!(selectedHotbarItem?.ProtoGameObject is IProtoItemToolToolbox))
            {
                // no tool is selected
                return;
            }

            actionState = new ConstructionActionState(character, (IStaticWorldObject)worldObject, selectedHotbarItem);
            if (!actionState.CheckIsNeeded())
            {
                // action is not needed
                Logger.Info($"Building/repairing is not required: {worldObject} by {character}", character);
                return;
            }

            if (!SharedCheckCanInteract(character, worldObject, writeToLog: true))
            {
                return;
            }

            if (!actionState.Config.IsAllowed)
            {
                // not allowed to build/repair
                // this code should never execute for a valid client
                Logger.Warning(
                    $"Building/repairing is not allowed by object build/repair config: {worldObject} by {character}",
                    character);

                if (Api.IsClient)
                {
                    NotificationSystem.ClientShowNotification(
                        // ReSharper disable once CanExtractXamlLocalizableStringCSharp
                        "Cannot construct",
                        // ReSharper disable once CanExtractXamlLocalizableStringCSharp
                        "Not constructable.",
                        NotificationColor.Bad,
                        selectedHotbarItem.ProtoItem.Icon);
                }

                return;
            }

            if (!actionState.ValidateRequiredItemsAvailable())
            {
                // action is not possible
                // logging is not required here - it's done automatically by the validation method
                return;
            }

            characterPrivateState.SetCurrentActionState(actionState);

            Logger.Info($"Building/repairing started: {worldObject} by {character}", character);

            if (IsClient)
            {
                // TODO: we need animation for building/repairing
                Instance.CallServer(_ => _.ServerRemote_StartAction(worldObject));
            }
        }
Esempio n. 16
0
        private static void SharedStartAction(ICharacter character, IWorldObject worldObject)
        {
            if (worldObject is null)
            {
                return;
            }

            if (!SharedCheckCanInteract(character, worldObject, writeToLog: true))
            {
                return;
            }

            if (worldObject.ProtoGameObject is not IProtoObjectStructure protoObjectStructure)
            {
                throw new Exception("Not a structure: " + worldObject);
            }

            if (!SharedIsDeconstructable(protoObjectStructure))
            {
                throw new Exception("Not deconstructable: " + worldObject);
            }

            var characterPrivateState = PlayerCharacter.GetPrivateState(character);
            var characterPublicState  = PlayerCharacter.GetPublicState(character);

            if (characterPrivateState.CurrentActionState is DeconstructionActionState actionState &&
                actionState.WorldObject == worldObject)
            {
                // already deconstructing
                return;
            }

            var selectedHotbarItem = characterPublicState.SelectedItem;

            if (selectedHotbarItem?.ProtoGameObject is not IProtoItemToolCrowbar)
            {
                selectedHotbarItem = null;
                if (worldObject.ProtoWorldObject is not ProtoObjectConstructionSite)
                {
                    // no crowbar tool is selected, only construction sites
                    // can be deconstructed without the crowbar
                    return;
                }
            }

            actionState = new DeconstructionActionState(character, (IStaticWorldObject)worldObject, selectedHotbarItem);
            if (!actionState.CheckIsAllowed(out var hasNoFactionPermission))
            {
                // not allowed to deconstruct
                Logger.Warning(
                    $"Deconstruction is not allowed: {worldObject} by {character}",
                    character);

                if (Api.IsClient)
                {
                    if (hasNoFactionPermission)
                    {
                        NotificationSystem.ClientShowNotification(
                            title: CoreStrings.Notification_ActionForbidden,
                            string.Format(CoreStrings.Faction_Permission_Required_Format,
                                          CoreStrings.Faction_Permission_LandClaimManagement_Title),
                            NotificationColor.Bad,
                            selectedHotbarItem?.ProtoItem.Icon);
                    }
                    else
                    {
                        NotificationSystem.ClientShowNotification(
                            NotificationCannotDeconstruct_Title,
                            LandClaimSystem.ErrorNotLandOwner_Message,
                            NotificationColor.Bad,
                            selectedHotbarItem?.ProtoItem.Icon);
                    }
                }

                return;
            }

            if (!actionState.CheckIsNeeded())
            {
                // action is not needed
                Logger.Important($"Deconstruction is not required: {worldObject} by {character}", character);
                return;
            }

            characterPrivateState.SetCurrentActionState(actionState);

            Logger.Important($"Deconstruction started: {worldObject} by {character}", character);

            if (IsClient)
            {
                // TODO: display crowbar started animation? Send animation to other players?
                Instance.CallServer(_ => _.ServerRemote_StartAction(worldObject));
            }
        }
Esempio n. 17
0
 private void ClientRemote_UnstuckSuccessful()
 {
     NotificationSystem.ClientShowNotification(
         NotificationUnstuckSuccessful_Title,
         NotificationUnstuckSuccessful_Message);
 }
        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);
                }
            }
        }
 private void ClientShowProtectedBagNotification()
 {
     NotificationSystem.ClientShowNotification(
         title: NewbieProtectionSystem.Notification_LootBagUnderProtection,
         icon: this.Icon);
 }