Exemplo n.º 1
0
        private void ServerRemote_OnClientInteractFinish(IWorldObject worldObject)
        {
            var character = ServerRemoteContext.Character;

            if (!InteractionCheckerSystem.SharedUnregister(character, worldObject, isAbort: false))
            {
                return;
            }

            Logger.Info($"Client {character} informed that the object interaction with {worldObject} is finished");
            this.ServerFinishInteractionInternal(character, worldObject);
        }
Exemplo n.º 2
0
        private IReadOnlyList <GarageVehicleEntry> ServerRemote_GetVehiclesList()
        {
            var character = ServerRemoteContext.Character;
            var currentInteractionObject = InteractionCheckerSystem.SharedGetCurrentInteraction(character);

            if (!(currentInteractionObject?.ProtoWorldObject is IProtoVehicleAssemblyBay))
            {
                Logger.Warning(character + " is not interacting with any vehicle assembly bay");
                return(Array.Empty <GarageVehicleEntry>());
            }

            var onlyVehiclesInGarage = !PveSystem.ServerIsPvE;

            return(ServerGetCharacterVehicles(character, onlyVehiclesInGarage));
        }
Exemplo n.º 3
0
        private void SharedAbortActionInternal(ICharacter character, TActionRequest request)
        {
            var characterPrivateState = PlayerCharacter.GetPrivateState(character);
            var state = characterPrivateState.CurrentActionState;

            if (!(state is TActionState actionState))
            {
                // no action or action of another state type
                return;
            }

            if (!request.Equals(actionState.Request))
            {
                // different request active
                return;
            }

            if (!state.IsCompleted)
            {
                // reset action state
                actionState.Cancel();
                // return now - because this method was just called again by SetCurrentActionState
                return;
            }

            // ensure the action state is reset
            characterPrivateState.SetCurrentActionState(null);

            Logger.Info("Action cancelled: " + state, character);

            if (state.TargetWorldObject is not null)
            {
                InteractionCheckerSystem.SharedUnregister(character, state.TargetWorldObject, isAbort: false);
            }

            if (IsClient &&
                !state.IsCancelledByServer)
            {
                Logger.Info("Sending action abort request: " + request);
                Instance.CallServer(_ => _.ServerRemote_AbortAction(request));
            }
            else if (IsServer &&
                     !ServerRemoteContext.IsRemoteCall)
            {
                Instance.CallClient(character, _ => _.ClientRemote_AbortAction(request));
                // TODO: notify other players as well
            }
        }
Exemplo n.º 4
0
        private bool ServerRemote_PutCurrentVehicle()
        {
            if (!PveSystem.ServerIsPvE)
            {
                throw new Exception("This feature is available only in PvE");
            }

            var character = ServerRemoteContext.Character;
            var currentInteractionObject = InteractionCheckerSystem.SharedGetCurrentInteraction(character);

            if (!(currentInteractionObject?.ProtoWorldObject is IProtoVehicleAssemblyBay protoVehicleAssemblyBay))
            {
                throw new Exception("Player is not interacting with an vehicle assembly bay");
            }

            using var tempVehiclesList = Api.Shared.GetTempList <IDynamicWorldObject>();
            protoVehicleAssemblyBay.SharedGetVehiclesOnPlatform(
                vehicleAssemblyBay: (IStaticWorldObject)currentInteractionObject,
                tempVehiclesList);

            var isPutAtLeastOne = false;

            foreach (var vehicle in tempVehiclesList.AsList())
            {
                if (ServerCanCharacterPutVehicleIntoGarage(vehicle, byCharacter: character))
                {
                    ServerPutIntoGarage(vehicle);
                    isPutAtLeastOne = true;
                }
            }

            if (isPutAtLeastOne)
            {
                // notify other players in scope
                var soundPosition = currentInteractionObject.TilePosition.ToVector2D()
                                    + protoVehicleAssemblyBay.PlatformCenterWorldOffset;
                using var tempPlayers = Api.Shared.GetTempList <ICharacter>();
                Server.World.GetScopedByPlayers(currentInteractionObject, tempPlayers);
                tempPlayers.Remove(character);

                Instance.CallClient(tempPlayers.AsList(),
                                    _ => _.ClientRemote_OnVehiclePutToGarageByOtherPlayer(soundPosition));
            }

            return(isPutAtLeastOne);
        }
Exemplo n.º 5
0
        public static async void ClientPutCurrentVehicle()
        {
            if (!PveSystem.ClientIsPve(logErrorIfDataIsNotYetAvailable: true))
            {
                // this feature is available only in PvE
                return;
            }

            var vehicleAssemblyBay = InteractionCheckerSystem.SharedGetCurrentInteraction(
                ClientCurrentCharacterHelper.Character);
            var isSuccess = await Instance.CallServer(_ => _.ServerRemote_PutCurrentVehicle());

            if (isSuccess)
            {
                Client.Audio.PlayOneShot(SoundResourcePutVehicle, vehicleAssemblyBay);
            }
        }
Exemplo n.º 6
0
        private bool ServerRemote_OnClientInteractStart(IWorldObject worldObject)
        {
            var character = ServerRemoteContext.Character;

            if (worldObject is null ||
                !worldObject.ProtoWorldObject.SharedCanInteract(character, worldObject, writeToLog: true))
            {
                // player is too far from the world object or world object is destroyed
                return(false);
            }

            InteractionCheckerSystem.SharedAbortCurrentInteraction(character);

            var proto = SharedGetProto(worldObject);

            proto.ServerOnClientInteract(character, worldObject);

            if (proto.IsAutoEnterPrivateScopeOnInteraction)
            {
                // enter private scope - containers will be sent to the player character
                Server.World.EnterPrivateScope(character, worldObject);
            }

            // register private scope exit on interaction cancel
            InteractionCheckerSystem.SharedRegister(
                character,
                worldObject,
                finishAction: isAbort =>
            {
                if (worldObject.IsDestroyed)
                {
                    return;
                }

                this.ServerFinishInteractionInternal(character, worldObject);

                if (isAbort)
                {
                    // notify client
                    this.CallClient(character, _ => this.ClientRemote_FinishInteraction(worldObject));
                }
            });

            Logger.Info($"Started object interaction with {worldObject} for {character}");
            return(true);
        }
Exemplo n.º 7
0
        private void ServerRemote_Repair()
        {
            var character         = ServerRemoteContext.Character;
            var tinkerTableObject =
                InteractionCheckerSystem.SharedGetCurrentInteraction(character) as IStaticWorldObject;

            this.VerifyGameObject(tinkerTableObject);

            var worldObjectPrivateState = GetPrivateState(tinkerTableObject);
            var containerInput          = worldObjectPrivateState.ContainerInput;
            var containerOutput         = worldObjectPrivateState.ContainerOutput;
            var inputItem1 = containerInput.GetItemAtSlot(0);
            var inputItem2 = containerInput.GetItemAtSlot(1);

            if (!ValidateCanRepair(character, tinkerTableObject, out var error))
            {
                Logger.Warning(tinkerTableObject + " cannot repair: " + error, character);
                return;
            }

            if (!CreativeModeSystem.SharedIsInCreativeMode(character))
            {
                InputItemsHelper.ServerDestroyItems(character, RequiredRepairComponentItems);
            }

            var resultDurabilityFraction = SharedCalculateResultDurabilityFraction(inputItem1, inputItem2, character);

            Server.Items.DestroyItem(inputItem2);
            Server.Items.MoveOrSwapItem(inputItem1,
                                        containerOutput,
                                        out _);

            var resultItemProto        = (IProtoItemWithDurablity)inputItem1.ProtoGameObject;
            var resultItemPrivateState = inputItem1.GetPrivateState <IItemWithDurabilityPrivateState>();

            resultItemPrivateState.DurabilityCurrent = (uint)Math.Round(
                resultDurabilityFraction * resultItemProto.DurabilityMax,
                MidpointRounding.AwayFromZero);

            character.ServerAddSkillExperience <SkillMaintenance>(
                SkillMaintenance.ExperiencePerItemRepaired);

            Logger.Info(
                $"Item repaired: {inputItem1}. Second item was destroyed to use for repair components: {inputItem2}");
        }
Exemplo n.º 8
0
        public void SharedOnActionCompleted(TActionState state)
        {
            var character = state.Character;
            var request   = state.Request;

            Logger.Info("Action completed: " + request, character);

            if (state.TargetWorldObject is not null)
            {
                InteractionCheckerSystem.SharedUnregister(character, state.TargetWorldObject, isAbort: false);
            }

            var canComplete = true;

            try
            {
                // ensure the request is still valid
                this.SharedValidateRequest(request);
            }
            catch (Exception ex)
            {
                canComplete = false;
                Logger.Warning("Exception during completed action processing: "
                               + ex.Message
                               + Environment.NewLine
                               + request);
            }

            if (canComplete)
            {
                try
                {
                    this.SharedOnActionCompletedInternal(state, character);
                }
                catch (Exception ex)
                {
                    Logger.Exception(ex, "Exception during completed action processing");
                }
            }

            PlayerCharacter.GetPrivateState(character)
            .SetCurrentActionState(null);
        }
Exemplo n.º 9
0
        /// <summary>
        /// Init on component enabled.
        /// </summary>
        public override void Start(ClientComponent parentComponent)
        {
            base.Start(parentComponent);

            // Check if there an action in progress.
            if (PrivateState.CurrentActionState != null)
            {
                readyForInteraction = false;
                lastActionState     = PrivateState.CurrentActionState;
            }

            // Check if we opened loot container before enabling component.
            var currentInteractionObject = InteractionCheckerSystem.SharedGetCurrentInteraction(CurrentCharacter);

            if (currentInteractionObject?.ProtoWorldObject is ProtoObjectLootContainer)
            {
                readyForInteraction = false;
            }
        }
Exemplo n.º 10
0
        public static async void ClientTakeVehicle(uint vehicleGameObjectId)
        {
            var vehicleAssemblyBay = InteractionCheckerSystem.SharedGetCurrentInteraction(
                ClientCurrentCharacterHelper.Character);

            var result = await Instance.CallServer(_ => _.ServerRemote_TakeVehicle(vehicleGameObjectId));

            if (result == TakeVehicleResult.Success)
            {
                Client.Audio.PlayOneShot(SoundResourceTakeVehicle, vehicleAssemblyBay);
                WindowObjectVehicleAssemblyBay.CloseActiveMenu();
                return;
            }

            if (result == TakeVehicleResult.BaseUnderRaidblock)
            {
                LandClaimSystem.SharedSendNotificationActionForbiddenUnderRaidblock(
                    ClientCurrentCharacterHelper.Character);
                return;
            }

            var error = result.GetDescription();

            if (string.IsNullOrEmpty(error))
            {
                return;
            }

            var currentInteractionObject =
                InteractionCheckerSystem.SharedGetCurrentInteraction(ClientCurrentCharacterHelper.Character);

            if (!(currentInteractionObject?.ProtoWorldObject is IProtoVehicleAssemblyBay protoVehicleAssemblyBay))
            {
                return;
            }

            NotificationSystem.ClientShowNotification(
                Notification_CannotTakeVehicle_Title,
                error,
                NotificationColor.Bad,
                protoVehicleAssemblyBay.Icon);
        }
Exemplo n.º 11
0
        private void ServerRemote_SetLightMode(IStaticWorldObject lightObject, ObjectLightMode mode)
        {
            var character = ServerRemoteContext.Character;

            if (!InteractionCheckerSystem.SharedHasInteraction(character, lightObject, requirePrivateScope: true))
            {
                throw new Exception("The player character is not interacting with the light object");
            }

            var privateState = GetPrivateState(lightObject);
            var publicState  = GetPublicState(lightObject);

            privateState.Mode = mode;
            Logger.Important($"Light mode changed: {mode}, light: {lightObject}", character);

            this.ServerUpdateLight(lightObject,
                                   privateState,
                                   publicState,
                                   deltaTime: 0);
        }
Exemplo n.º 12
0
        private void ClientRemote_OnContainerOpened(IStaticWorldObject worldObject)
        {
            var privateState   = GetPrivateState(worldObject);
            var itemsContainer = privateState.ItemsContainer;

            var soundClose = Client.UI.GetApplicationResource <SoundUI>("SoundWindowContainerClose");
            var menuWindow = WindowContainerExchange.Show(
                itemsContainer,
                soundClose: soundClose,
                isAutoClose: true);

            var character = Client.Characters.CurrentPlayerCharacter;

            InteractionCheckerSystem.SharedRegister(
                character,
                worldObject,
                finishAction: _ => menuWindow.CloseWindow());

            ClientInteractionUISystem.Register(
                worldObject,
                menuWindow,
                onMenuClosedByClient:
                () =>
            {
                InteractionCheckerSystem.SharedUnregister(character, worldObject, isAbort: false);
                if (!worldObject.IsDestroyed)
                {
                    this.CallServer(_ => _.ServerRemote_OnClientInteractFinish(worldObject));
                }
            });

            Logger.Important("Started object interaction with " + worldObject);

            ClientCurrentInteractionMenu.RegisterMenuWindow(menuWindow);
            ClientCurrentInteractionMenu.Open();
        }
Exemplo n.º 13
0
        public static void ServerAddEnergyCharge(IItemsContainer container, double energyAmountToAdd)
        {
            if (energyAmountToAdd <= 0 ||
                container == null ||
                container.IsDestroyed)
            {
                return;
            }

            ITempList <IItem> tempItemsList;

            if (container.Owner is ICharacter ownerCharacter)
            {
                tempItemsList = SharedGetTempListPowerBanksForCharacter(ownerCharacter, onlyEquippedDevices: false);
            }
            else
            {
                tempItemsList = Api.Shared.GetTempList <IItem>();
                SharedFindPowerBanks(container, tempItemsList);

                if (container.Owner is IWorldObject worldObject)
                {
                    foreach (var character in InteractionCheckerSystem.SharedEnumerateCurrentInteractionCharacters(
                                 worldObject))
                    {
                        using var tempCharacterPowerBanks = SharedGetTempListPowerBanksForCharacter(character,
                                                                                                    onlyEquippedDevices:
                                                                                                    false);
                        tempItemsList.AddRange(tempCharacterPowerBanks.AsList());
                    }
                }
            }

            ServerAddEnergyInternal(energyAmountToAdd, tempItemsList.AsList());
            tempItemsList.Dispose();
        }
Exemplo n.º 14
0
        /// <summary>
        /// Check if the character's interaction area collides with the world object click area.
        /// The character also should not be dead.
        /// </summary>
        public bool SharedIsInsideCharacterInteractionArea(
            ICharacter character,
            TWorldObject worldObject,
            bool writeToLog,
            CollisionGroup requiredCollisionGroup = null)
        {
            if (worldObject.IsDestroyed)
            {
                return(false);
            }

            try
            {
                this.VerifyGameObject(worldObject);
            }
            catch (Exception ex)
            {
                if (writeToLog)
                {
                    Logger.Exception(ex);
                }
                else
                {
                    Logger.Warning(ex.Message + " during " + nameof(SharedIsInsideCharacterInteractionArea));
                }

                return(false);
            }

            bool isInsideInteractionArea;

            if (worldObject.PhysicsBody.HasShapes)
            {
                // check that the world object is inside the interaction area of the character
                using (var objectsInCharacterInteractionArea
                           = InteractionCheckerSystem.SharedGetTempObjectsInCharacterInteractionArea(
                                 character,
                                 writeToLog,
                                 requiredCollisionGroup))
                {
                    isInsideInteractionArea =
                        objectsInCharacterInteractionArea?.Any(t => t.PhysicsBody.AssociatedWorldObject == worldObject)
                        ?? false;
                }
            }
            else
            {
                // the world object doesn't have physics shapes
                // check this object tile by tile
                // ensure at least one tile of this object is inside the character interaction area
                // ensure there is direct line of sight between player character and this tile

                var characterInteractionAreaShape = character.PhysicsBody.Shapes.FirstOrDefault(
                    s => s.CollisionGroup
                    == CollisionGroups.CharacterInteractionArea);
                isInsideInteractionArea = false;
                foreach (var tileOffset in ((IProtoStaticWorldObject)worldObject.ProtoWorldObject).Layout.TileOffsets)
                {
                    var penetration = character.PhysicsBody.PhysicsSpace.TestShapeCollidesWithShape(
                        sourceShape: characterInteractionAreaShape,
                        targetShape: new RectangleShape(
                            position: (worldObject.TilePosition + tileOffset).ToVector2D()
                            + (0.01, 0.01),
                            size: (0.98, 0.98),
                            collisionGroup: CollisionGroups.ClickArea),
                        sourceShapeOffset: character.PhysicsBody.Position);

                    if (!penetration.HasValue)
                    {
                        // this tile is not inside the character interaction area
                        continue;
                    }

                    // the tile is inside the character interaction area
                    // check that there is a direct line between the character and the tile
                    isInsideInteractionArea = true;
                    break;
                }
            }

            if (!isInsideInteractionArea)
            {
                // the world object is outside the character interaction area
                if (writeToLog)
                {
                    Logger.Warning(
                        $"Character cannot interact with {worldObject} - outside the interaction area.",
                        character);

                    if (IsClient)
                    {
                        this.ClientOnCannotInteract(worldObject, CoreStrings.Notification_TooFar, isOutOfRange: true);
                    }
                }

                return(false);
            }

            // check that there are no other objects on the way between them (defined by default layer)
            var physicsSpace      = character.PhysicsBody.PhysicsSpace;
            var characterCenter   = character.Position + character.PhysicsBody.CenterOffset;
            var worldObjectCenter = worldObject.TilePosition.ToVector2D() + worldObject.PhysicsBody.CenterOffset;
            var worldObjectPointClosestToCharacter = worldObject.PhysicsBody.ClampPointInside(
                characterCenter,
                CollisionGroups.Default,
                out var isSuccess);

            if (!isSuccess)
            {
                // the physics body seems to not have the default collider, let's check for the click area instead
                worldObjectPointClosestToCharacter = worldObject.PhysicsBody.ClampPointInside(
                    characterCenter,
                    CollisionGroups.ClickArea,
                    out _);
            }

            // local method for testing if there is an obstacle from current to the specified position
            bool TestHasObstacle(Vector2D toPosition)
            {
                using (var obstaclesOnTheWay = physicsSpace.TestLine(
                           characterCenter,
                           toPosition,
                           CollisionGroup.GetDefault(),
                           sendDebugEvent: writeToLog))
                {
                    foreach (var test in obstaclesOnTheWay)
                    {
                        var testPhysicsBody = test.PhysicsBody;
                        if (testPhysicsBody.AssociatedProtoTile != null)
                        {
                            // obstacle tile on the way
                            return(true);
                        }

                        var testWorldObject = testPhysicsBody.AssociatedWorldObject;
                        if (testWorldObject == character ||
                            testWorldObject == worldObject)
                        {
                            // not an obstacle - it's the character or world object itself
                            continue;
                        }

                        if (!this.CommonIsAllowedObjectToInteractThrought(testWorldObject))
                        {
                            // obstacle object on the way
                            return(true);
                        }
                    }

                    // no obstacles
                    return(false);
                }
            }

            if (character.ProtoCharacter is PlayerCharacterSpectator)
            {
                // don't test for obstacles for spectator character
                return(true);
            }

            // let's test by casting rays from character center to:
            // 0) world object center
            // 1) world object point closest to the character
            // 2) combined - take X from center, take Y from closest
            // 3) combined - take X from closest, take Y from center
            if (TestHasObstacle(worldObjectCenter) &&
                TestHasObstacle(worldObjectPointClosestToCharacter) &&
                TestHasObstacle((worldObjectCenter.X,
                                 worldObjectPointClosestToCharacter.Y)) &&
                TestHasObstacle((worldObjectPointClosestToCharacter.X, worldObjectCenter.Y)))
            {
                // has obstacle
                if (writeToLog)
                {
                    Logger.Warning(
                        $"Character cannot interact with {worldObject} - there are other objects on the way.",
                        character);

                    if (IsClient)
                    {
                        this.ClientOnCannotInteract(worldObject,
                                                    CoreStrings.Notification_ObstaclesOnTheWay,
                                                    isOutOfRange: true);
                    }
                }

                return(false);
            }

            if (character.GetPublicState <ICharacterPublicState>().IsDead)
            {
                // character is dead
                if (writeToLog)
                {
                    Logger.Warning(
                        $"Character cannot interact with {worldObject} - character is dead.",
                        character);
                }

                return(false);
            }

            return(true);
        }
        public static string ServerSetOwners(
            IWorldObject worldObject,
            List <string> newOwners,
            NetworkSyncList <string> currentOwners,
            ICharacter byOwner)
        {
            var protoObject = (IProtoObjectWithOwnersList)worldObject.ProtoGameObject;

            if (!protoObject.HasOwnersList)
            {
                throw new Exception("This object doesn't support owners list: " + worldObject);
            }

            if (!InteractionCheckerSystem.SharedHasInteraction(ServerRemoteContext.Character,
                                                               worldObject,
                                                               requirePrivateScope: true))
            {
                throw new Exception("The player character is not interacting with " + worldObject);
            }

            if (!currentOwners.Contains(byOwner.Name) &&
                !CreativeModeSystem.SharedIsInCreativeMode(byOwner))
            {
                return(DialogCannotSetOwners_MessageNotOwner);
            }

            if (!((IProtoObjectWithOwnersList)worldObject.ProtoGameObject)
                .SharedCanEditOwners(worldObject, byOwner))
            {
                return(DialogCannotSetOwners_MessageCannotEdit);
            }

            currentOwners.GetDiff(newOwners, out var ownersToAdd, out var ownersToRemove);
            if (ownersToRemove.Count > 0 &&
                currentOwners.Count == ownersToRemove.Count)
            {
                return(DialogCannotSetOwners_MessageCannotRemoveLastOwner);
            }

            if (ownersToRemove.Contains(byOwner.Name))
            {
                return(DialogCannotSetOwners_MessageCannotRemoveSelf);
            }

            if (ownersToAdd.Count == 0 &&
                ownersToRemove.Count == 0)
            {
                Logger.Warning(
                    "No need to change the owners - the new owners list is the same as the current owners list: "
                    + worldObject,
                    characterRelated: byOwner);
                return(null);
            }

            foreach (var n in ownersToAdd)
            {
                var name        = n;
                var playerToAdd = Api.Server.Characters.GetPlayerCharacter(name);
                if (playerToAdd == null)
                {
                    return(string.Format(DialogCannotSetOwners_MessageFormatPlayerNotFound, name));
                }

                // get proper player name
                name = playerToAdd.Name;
                if (currentOwners.AddIfNotContains(name))
                {
                    Api.Logger.Important($"Added owner: {name}; {worldObject}", characterRelated: byOwner);
                }
            }

            foreach (var name in ownersToRemove)
            {
                if (!currentOwners.Remove(name))
                {
                    continue;
                }

                Api.Logger.Important($"Removed owner: {name}; {worldObject}", characterRelated: byOwner);

                var removedPlayer = Api.Server.Characters.GetPlayerCharacter(name);
                if (removedPlayer == null)
                {
                    continue;
                }

                InteractableWorldObjectHelper.ServerTryAbortInteraction(removedPlayer, worldObject);
            }

            ServerInvokeOwnersChangedEvent(worldObject);
            return(null);
        }
Exemplo n.º 16
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);
        }
Exemplo n.º 17
0
        private void ServerRemote_Uninstall(byte slotId)
        {
            var character   = ServerRemoteContext.Character;
            var worldObject = InteractionCheckerSystem.GetCurrentInteraction(character);

            this.VerifyGameObject((IStaticWorldObject)worldObject);

            var itemToUninstall = character.SharedGetPlayerContainerEquipment()
                                  .GetItemAtSlot(slotId);

            if (itemToUninstall == null)
            {
                throw new Exception("No implant installed");
            }

            var itemToUninstallProto      = (IProtoItemEquipmentImplant)itemToUninstall.ProtoItem;
            var biomaterialRequiredAmount = CreativeModeSystem.SharedIsInCreativeMode(character)
                                                ? (ushort)0
                                                : itemToUninstallProto.BiomaterialAmountRequiredToUninstall;

            if (!character.ContainsItemsOfType(ProtoItemBiomaterialVial.Value,
                                               requiredCount: biomaterialRequiredAmount) &&
                !CreativeModeSystem.SharedIsInCreativeMode(character))
            {
                throw new Exception("Not enough biomaterial vials");
            }

            // move to inventory
            if (!Server.Items.MoveOrSwapItem(itemToUninstall,
                                             character.SharedGetPlayerContainerInventory(),
                                             out _))
            {
                NotificationSystem.ServerSendNotificationNoSpaceInInventory(character);
                return;
            }

            if (biomaterialRequiredAmount > 0)
            {
                // destroy vials
                Server.Items.DestroyItemsOfType(character,
                                                ProtoItemBiomaterialVial.Value,
                                                countToDestroy: biomaterialRequiredAmount,
                                                out _);

                NotificationSystem.ServerSendItemsNotification(
                    character,
                    ProtoItemBiomaterialVial.Value,
                    -biomaterialRequiredAmount);
            }

            if (itemToUninstallProto is ItemImplantBroken)
            {
                // broken implant destroys on uninstall
                Server.Items.DestroyItem(itemToUninstall);
                NotificationSystem.ServerSendItemsNotification(
                    character,
                    itemToUninstallProto,
                    -1);
            }

            Logger.Info("Implant uninstalled: " + itemToUninstall);
        }
Exemplo n.º 18
0
        private void ServerRemote_Install(IItem itemToInstall, byte slotId)
        {
            var character   = ServerRemoteContext.Character;
            var worldObject = InteractionCheckerSystem.GetCurrentInteraction(character);

            this.VerifyGameObject((IStaticWorldObject)worldObject);

            var containerEquipment   = character.SharedGetPlayerContainerEquipment();
            var currentInstalledItem = containerEquipment.GetItemAtSlot(slotId);

            if (currentInstalledItem != null)
            {
                if (currentInstalledItem == itemToInstall)
                {
                    Logger.Info("The implant is already installed");
                    return;
                }

                throw new Exception("Please uninstall installed implant");
            }

            if (itemToInstall.Container.OwnerAsCharacter != character ||
                itemToInstall.Container == containerEquipment)
            {
                throw new Exception("The item to install must be in character containers (except equipment)");
            }

            if (!containerEquipment.ProtoItemsContainer.CanAddItem(
                    new CanAddItemContext(containerEquipment,
                                          itemToInstall,
                                          slotId,
                                          byCharacter: null,
                                          isExploratoryCheck: false)))
            {
                throw new Exception("Cannot install implant item there");
            }

            var itemToInstallProto = (IProtoItemEquipmentImplant)itemToInstall.ProtoItem;

            if (itemToInstallProto is ItemImplantBroken)
            {
                throw new Exception("Cannot install broken implant");
            }

            foreach (var equippedItem in containerEquipment.Items)
            {
                if (equippedItem.ProtoItem == itemToInstallProto)
                {
                    throw new Exception("Another implant of this type is already installed: " + itemToInstallProto);
                }
            }

            var biomaterialRequiredAmount = CreativeModeSystem.SharedIsInCreativeMode(character)
                                                ? (ushort)0
                                                : itemToInstallProto.BiomaterialAmountRequiredToInstall;

            if (!character.ContainsItemsOfType(ProtoItemBiomaterialVial.Value,
                                               requiredCount: biomaterialRequiredAmount))
            {
                throw new Exception("Not enough biomaterial vials");
            }

            if (!Server.Items.MoveOrSwapItem(itemToInstall,
                                             containerEquipment,
                                             out _,
                                             slotId: slotId))
            {
                throw new Exception("Unknown error - cannot move implant item to the player equipment");
            }

            if (biomaterialRequiredAmount > 0)
            {
                // destroy vials
                Server.Items.DestroyItemsOfType(character,
                                                ProtoItemBiomaterialVial.Value,
                                                countToDestroy: biomaterialRequiredAmount,
                                                out _);

                NotificationSystem.ServerSendItemsNotification(
                    character,
                    ProtoItemBiomaterialVial.Value,
                    -biomaterialRequiredAmount);
            }

            Logger.Info("Implant installed: " + itemToInstall);
        }
Exemplo n.º 19
0
        public bool SharedStartAction(TActionRequest request)
        {
            if (request == null)
            {
                return(false);
            }

            var character = request.Character;

            var characterPrivateState = PlayerCharacter.GetPrivateState(character);

            if (characterPrivateState.CurrentActionState is TActionState existingState &&
                this.SharedIsSameAction(existingState, request))
            {
                // the same action is already in process
                Logger.Info(
                    $"Action cannot be started: {request} - already performing the same action",
                    character);
                return(false);
            }

            try
            {
                this.SharedValidateRequest(request);
            }
            catch (Exception ex)
            {
                Logger.Info(
                    $"Action cannot be started: {request} the request is not valid: {Environment.NewLine}{ex.Message}",
                    character);
                return(false);
            }

            var state = this.SharedTryCreateState(request);

            if (state == null)
            {
                Logger.Info(
                    "Action cannot be started: " + request,
                    character);
                return(false);
            }

            state.Request = request;
            InteractionCheckerSystem.CancelCurrentInteraction(character);
            characterPrivateState.SetCurrentActionState(state);

            Logger.Info("Action started: " + request, character);

            if (IsClient)
            {
                this.ClientOnStartActionCompleted(request, state);
            }

            if (state.TargetWorldObject != null)
            {
                InteractionCheckerSystem.Register(
                    character,
                    state.TargetWorldObject,
                    finishAction:
                    isAbort =>
                {
                    if (!isAbort)
                    {
                        return;
                    }

                    Logger.Warning(
                        $"InteractionCheckerSystem interaction check failed - cancelling \"{this.Name}\" action: {state}",
                        character);
                    this.SharedAbortActionInternal(character, state.Request);
                });
            }

            return(true);
        }
Exemplo n.º 20
0
        private TakeVehicleResult ServerRemote_TakeVehicle(uint vehicleGameObjectId)
        {
            var character = ServerRemoteContext.Character;
            var currentInteractionObject = InteractionCheckerSystem.SharedGetCurrentInteraction(character);

            if (!(currentInteractionObject?.ProtoWorldObject is IProtoVehicleAssemblyBay protoVehicleAssemblyBay))
            {
                Logger.Warning("Player is not interacting with an vehicle assembly bay", character);
                return(TakeVehicleResult.Unknown);
            }

            var vehicleAssemblyBay = (IStaticWorldObject)currentInteractionObject;
            var vehicle            = Server.World.GetGameObjectById <IDynamicWorldObject>(GameObjectType.DynamicObject,
                                                                                          vehicleGameObjectId);

            if (vehicle == null)
            {
                Logger.Warning("Vehicle is not found", character);
                return(TakeVehicleResult.Unknown);
            }

            if (!WorldObjectOwnersSystem.SharedIsOwner(character, vehicle))
            {
                Logger.Warning("Not an owner of the vehicle: " + vehicle, character);
                return(TakeVehicleResult.NotOwner);
            }

            var status = ServerGetVehicleStatus(vehicle, forCharacter: character);

            switch (status)
            {
            case VehicleStatus.InGarage:
                // allow to take
                break;

            case VehicleStatus.InWorld:
                if (!PveSystem.ServerIsPvE)
                {
                    Logger.Warning("Cannot take a vehicle from world on a PvP server", character);
                    return(TakeVehicleResult.Unknown);
                }

                // allow to take a vehicle from world in PvE only
                break;

            case VehicleStatus.InUse:
                return(TakeVehicleResult.Error_InUse);

            case VehicleStatus.Docked:
                return(TakeVehicleResult.Error_Docked);

            default:
                return(TakeVehicleResult.Unknown);
            }

            if (IsServer &&
                LandClaimSystem.SharedIsUnderRaidBlock(character, vehicleAssemblyBay))
            {
                return(TakeVehicleResult.BaseUnderRaidblock);
            }

            if (protoVehicleAssemblyBay.SharedIsBaySpaceBlocked(
                    vehicleAssemblyBay: vehicleAssemblyBay))
            {
                return(TakeVehicleResult.SpaceBlocked);
            }

            var position = currentInteractionObject.TilePosition.ToVector2D()
                           + protoVehicleAssemblyBay.PlatformCenterWorldOffset;

            var vehiclePrivateState = vehicle.GetPrivateState <VehiclePrivateState>();

            vehiclePrivateState.IsInGarage = false;
            vehiclePrivateState.ServerTimeSincePilotOffline = 0;
            vehiclePrivateState.ServerTimeSinceLastUse      = ThresholdNoPilotSeconds + 1;

            Server.World.SetPosition(vehicle,
                                     position,
                                     writeToLog: false);

            vehicle.ProtoWorldObject.SharedCreatePhysics(vehicle);
            Logger.Important("Vehicle taken out of the garage: " + vehicle, character);

            // notify other players in scope
            var soundPosition = currentInteractionObject.TilePosition.ToVector2D()
                                + protoVehicleAssemblyBay.PlatformCenterWorldOffset;

            using var tempPlayers = Api.Shared.GetTempList <ICharacter>();
            Server.World.GetScopedByPlayers(currentInteractionObject, tempPlayers);
            tempPlayers.Remove(character);

            Instance.CallClient(tempPlayers.AsList(),
                                _ => _.ClientRemote_OnVehicleTakenFromGarageByOtherPlayer(soundPosition));

            return(TakeVehicleResult.Success);
        }
Exemplo n.º 21
0
        public ObjectLandClaimCanUpgradeCheckResult SharedCanUpgrade(
            IStaticWorldObject worldObjectLandClaim,
            IProtoObjectLandClaim protoUpgradedLandClaim,
            ICharacter character,
            out IConstructionUpgradeEntryReadOnly upgradeEntry,
            bool writeErrors = true)
        {
            if (!this.SharedCanInteract(character,
                                        worldObjectLandClaim,
                                        writeToLog: writeErrors))
            {
                upgradeEntry = null;
                return(ObjectLandClaimCanUpgradeCheckResult.ErrorUnknown);
            }

            upgradeEntry = null;
            foreach (var entry in this.ConfigUpgrade.Entries)
            {
                if (entry.ProtoStructure == protoUpgradedLandClaim)
                {
                    upgradeEntry = entry;
                    break;
                }
            }

            var currentLandClaimArea = GetPublicState(worldObjectLandClaim).LandClaimAreaObject;
            var founderName          = LandClaimArea.GetPrivateState(currentLandClaimArea).LandClaimFounder;

            var result = ObjectLandClaimCanUpgradeCheckResult.Success;

            if (upgradeEntry == null)
            {
                result = ObjectLandClaimCanUpgradeCheckResult.ErrorUnknown;
            }

            if (result == ObjectLandClaimCanUpgradeCheckResult.Success)
            {
                if (character.Name != founderName &&
                    !CreativeModeSystem.SharedIsInCreativeMode(character))
                {
                    result = ObjectLandClaimCanUpgradeCheckResult.ErrorNotFounder;
                }
            }

            if (result == ObjectLandClaimCanUpgradeCheckResult.Success)
            {
                // validate player know the tech, have enough items, etc
                if (!upgradeEntry.CheckRequirementsSatisfied(character))
                {
                    upgradeEntry = null;
                    result       = ObjectLandClaimCanUpgradeCheckResult.ErrorRequirementsNotSatisfied;
                }
            }

            if (result == ObjectLandClaimCanUpgradeCheckResult.Success)
            {
                // check there will be no intersection with other areas
                var landClaimCenterTilePosition =
                    LandClaimSystem.SharedCalculateLandClaimObjectCenterTilePosition(worldObjectLandClaim);
                if (!LandClaimSystem.SharedCheckCanPlaceOrUpgradeLandClaimThere(
                        protoUpgradedLandClaim,
                        landClaimCenterTilePosition,
                        character))
                {
                    result = ObjectLandClaimCanUpgradeCheckResult.ErrorAreaIntersection;
                }
            }

            if (result == ObjectLandClaimCanUpgradeCheckResult.Success)
            {
                if (!InteractionCheckerSystem.SharedHasInteraction(character,
                                                                   worldObjectLandClaim,
                                                                   requirePrivateScope: true))
                {
                    result = ObjectLandClaimCanUpgradeCheckResult.ErrorNoActiveInteraction;
                }
            }

            if (result == ObjectLandClaimCanUpgradeCheckResult.Success)
            {
                if (LandClaimSystem.SharedIsUnderRaidBlock(character, worldObjectLandClaim))
                {
                    // the building is in an area under the raid
                    LandClaimSystem.SharedSendNotificationActionForbiddenUnderRaidblock(character);
                    result = ObjectLandClaimCanUpgradeCheckResult.ErrorUnderRaid;
                }
            }

            if (writeErrors &&
                result != ObjectLandClaimCanUpgradeCheckResult.Success)
            {
                Logger.Warning(
                    $"Can\'t upgrade: {worldObjectLandClaim} to {protoUpgradedLandClaim}: error code - {result}",
                    character);
            }

            return(result);
        }
Exemplo n.º 22
0
        private void ServerRemote_Repair()
        {
            var character         = ServerRemoteContext.Character;
            var tinkerTableObject =
                InteractionCheckerSystem.SharedGetCurrentInteraction(character) as IStaticWorldObject;

            this.VerifyGameObject(tinkerTableObject);

            var worldObjectPrivateState = GetPrivateState(tinkerTableObject);
            var containerInput          = worldObjectPrivateState.ContainerInput;
            var containerOutput         = worldObjectPrivateState.ContainerOutput;
            var inputItem1 = containerInput.GetItemAtSlot(0);
            var inputItem2 = containerInput.GetItemAtSlot(1);

            if (!ValidateCanRepair(character, tinkerTableObject, out var error))
            {
                Logger.Warning(tinkerTableObject + " cannot repair: " + error, character);
                return;
            }

            if (!CreativeModeSystem.SharedIsInCreativeMode(character))
            {
                InputItemsHelper.ServerDestroyItems(character, RequiredRepairComponentItems);
            }

            var resultItemProto          = (IProtoItemWithDurability)inputItem1.ProtoGameObject;
            var resultDurabilityFraction = SharedCalculateResultDurabilityFraction(inputItem1, inputItem2, character);

            // break the second input item (it will force spawning ammo in this slot if it's a loaded weapon)
            ItemDurabilitySystem.ServerModifyDurability(inputItem2,
                                                        -(double)resultItemProto.DurabilityMax,
                                                        roundUp: false);
            if (!inputItem2.IsDestroyed)
            {
                // ensure the second input item is destroyed
                Server.Items.DestroyItem(inputItem2);
            }

            Server.Items.MoveOrSwapItem(inputItem1,
                                        containerOutput,
                                        out _);

            var resultItemPrivateState = inputItem1.GetPrivateState <IItemWithDurabilityPrivateState>();

            if (resultDurabilityFraction < 1 &&
                character.SharedHasSkillFlag(SkillMaintenance.Flags.ChanceToRepairCompletely) &&
                RandomHelper.RollWithProbability(0.05))
            {
                // 5% chance to repair item completely rolled successfully
                resultDurabilityFraction = 1;
                this.CallClient(character,
                                _ => _.ClientRemote_ItemRepairedCompletely(inputItem1.ProtoItem));
            }

            resultItemPrivateState.DurabilityCurrent = (uint)Math.Round(
                resultDurabilityFraction * resultItemProto.DurabilityMax,
                MidpointRounding.AwayFromZero);

            character.ServerAddSkillExperience <SkillMaintenance>(
                SkillMaintenance.ExperiencePerItemRepaired);

            Logger.Info(
                $"Item repaired: {inputItem1}. Second item was destroyed to use for repair components: {inputItem2}");
        }
Exemplo n.º 23
0
 public static void ServerTryAbortInteraction(ICharacter character, IWorldObject worldObject)
 {
     InteractionCheckerSystem.SharedUnregister(character, worldObject, isAbort: true);
 }
Exemplo n.º 24
0
        private async void ClientInteractStartAsync(IStaticWorldObject worldObject)
        {
            if (this.isAwaitingServerInteraction)
            {
                return;
            }

            var character = Client.Characters.CurrentPlayerCharacter;

            if (InteractionCheckerSystem.GetCurrentInteraction(character) == worldObject)
            {
                // already interacting with this object
                return;
            }

            this.isAwaitingServerInteraction = true;
            try
            {
                var requestId = ++lastRequestId;
                var isOpened  = await this.CallServer(_ => _.ServerRemote_OnClientInteractStart(worldObject));

                if (!isOpened ||
                    requestId != lastRequestId)
                {
                    return;
                }
            }
            finally
            {
                this.isAwaitingServerInteraction = false;
            }

            var menuWindow = SharedGetProto(worldObject).ClientOpenUI(worldObject);

            if (menuWindow == null)
            {
                Logger.Important("Cannot open menu for object interaction with " + worldObject);
                this.CallServer(_ => _.ServerRemote_OnClientInteractFinish(worldObject));
                return;
            }

            ClientCurrentInteractionMenu.RegisterMenuWindow(menuWindow);

            InteractionCheckerSystem.Register(
                character,
                worldObject,
                finishAction: _ => menuWindow.CloseWindow());

            ClientInteractionUISystem.Register(
                worldObject,
                menuWindow,
                onMenuClosedByClient:
                () =>
            {
                InteractionCheckerSystem.Unregister(character, worldObject, isAbort: false);
                if (!worldObject.IsDestroyed)
                {
                    ++lastRequestId;
                    this.CallServer(_ => _.ServerRemote_OnClientInteractFinish(worldObject));
                }
            });

            Logger.Important("Started object interaction with " + worldObject);
            ClientCurrentInteractionMenu.Open();
        }
Exemplo n.º 25
0
        private async void ClientInteractStartAsync(IWorldObject worldObject)
        {
            if (this.isAwaitingServerInteraction)
            {
                return;
            }

            var character = Client.Characters.CurrentPlayerCharacter;

            if (InteractionCheckerSystem.SharedGetCurrentInteraction(character) == worldObject)
            {
                // already interacting with this object
                return;
            }

            this.isAwaitingServerInteraction = true;
            try
            {
                var requestId = ++lastRequestId;
                var isOpened  = await this.CallServer(_ => _.ServerRemote_OnClientInteractStart(worldObject));

                if (!isOpened ||
                    requestId != lastRequestId)
                {
                    return;
                }
            }
            finally
            {
                this.isAwaitingServerInteraction = false;
            }

            var objectWindow = SharedGetProto(worldObject).ClientOpenUI(worldObject);

            if (objectWindow is null)
            {
                Logger.Info("Cannot open menu for object interaction with " + worldObject);
                this.CallServer(_ => _.ServerRemote_OnClientInteractFinish(worldObject));
                return;
            }

            Api.SafeInvoke(() => ClientMenuCreated?.Invoke(worldObject, objectWindow));
            if (!(objectWindow is IMenu))
            {
                ClientCurrentInteractionMenu.RegisterMenuWindow(objectWindow);
            }
            else
            {
                ClientCurrentInteractionMenu.TryCloseCurrentMenu();
            }

            InteractionCheckerSystem.SharedRegister(
                character,
                worldObject,
                finishAction: _ => objectWindow.CloseWindow());

            ClientInteractionUISystem.Register(
                worldObject,
                objectWindow,
                onMenuClosedByClient:
                () =>
            {
                InteractionCheckerSystem.SharedUnregister(character, worldObject, isAbort: false);
                if (!worldObject.IsDestroyed)
                {
                    ++lastRequestId;
                    this.CallServer(_ => _.ServerRemote_OnClientInteractFinish(worldObject));
                }
            });

            Logger.Info("Started object interaction with " + worldObject);
            if (objectWindow is IMenu objectMenu)
            {
                if (!objectMenu.IsOpened)
                {
                    objectMenu.Toggle();
                }
            }
            else
            {
                ClientCurrentInteractionMenu.Open();
            }
        }
Exemplo n.º 26
0
        /// <summary>
        /// Check if the character's interaction area collides with the world object click area.
        /// The character also should not be dead.
        /// </summary>
        public virtual bool SharedIsInsideCharacterInteractionArea(
            ICharacter character,
            TWorldObject worldObject,
            bool writeToLog,
            CollisionGroup requiredCollisionGroup = null)
        {
            if (worldObject.IsDestroyed)
            {
                return(false);
            }

            try
            {
                this.VerifyGameObject(worldObject);
            }
            catch (Exception ex)
            {
                if (writeToLog)
                {
                    Logger.Exception(ex);
                }
                else
                {
                    Logger.Warning(ex.Message + " during " + nameof(SharedIsInsideCharacterInteractionArea));
                }

                return(false);
            }

            if (character.GetPublicState <ICharacterPublicState>().IsDead ||
                IsServer && !character.ServerIsOnline)
            {
                if (writeToLog)
                {
                    Logger.Warning(
                        $"Character cannot interact with {worldObject} - character is dead or offline.",
                        character);
                }

                return(false);
            }

            bool isInsideInteractionArea;

            if (worldObject.PhysicsBody.HasShapes &&
                worldObject.PhysicsBody.HasAnyShapeCollidingWithGroup(CollisionGroups.ClickArea))
            {
                // check that the world object is inside the interaction area of the character
                using var objectsInCharacterInteractionArea
                          = InteractionCheckerSystem.SharedGetTempObjectsInCharacterInteractionArea(
                                character,
                                writeToLog,
                                requiredCollisionGroup);

                isInsideInteractionArea = false;
                if (objectsInCharacterInteractionArea is not null)
                {
                    foreach (var t in objectsInCharacterInteractionArea.AsList())
                    {
                        if (!ReferenceEquals(worldObject, t.PhysicsBody.AssociatedWorldObject))
                        {
                            continue;
                        }

                        isInsideInteractionArea = true;
                        break;
                    }
                }
            }
            else if (worldObject.ProtoWorldObject is IProtoStaticWorldObject protoStaticWorldObject)
            {
                // the world object doesn't have click area collision shapes
                // check this object tile by tile
                // ensure at least one tile of this object is inside the character interaction area
                // ensure there is direct line of sight between player character and this tile

                var characterInteractionAreaShape = character.PhysicsBody.Shapes.FirstOrDefault(
                    s => s.CollisionGroup
                    == CollisionGroups.CharacterInteractionArea);
                isInsideInteractionArea = false;
                foreach (var tileOffset in protoStaticWorldObject.Layout.TileOffsets)
                {
                    var penetration = character.PhysicsBody.PhysicsSpace.TestShapeCollidesWithShape(
                        sourceShape: characterInteractionAreaShape,
                        targetShape: new RectangleShape(
                            position: (worldObject.TilePosition + tileOffset).ToVector2D()
                            + (0.01, 0.01),
                            size: (0.98, 0.98),
                            collisionGroup: CollisionGroups.ClickArea),
                        sourceShapeOffset: character.PhysicsBody.Position);

                    if (!penetration.HasValue)
                    {
                        // this tile is not inside the character interaction area
                        continue;
                    }

                    // the tile is inside the character interaction area
                    // check that there is a direct line between the character and the tile
                    isInsideInteractionArea = true;
                    break;
                }
            }
            else
            {
                isInsideInteractionArea = false;
            }

            if (!isInsideInteractionArea)
            {
                // the world object is outside the character interaction area
                if (writeToLog)
                {
                    Logger.Warning(
                        $"Character cannot interact with {worldObject} - outside the interaction area.",
                        character);

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

                return(false);
            }

            if (character.ProtoCharacter is PlayerCharacterSpectator)
            {
                // don't test for obstacles for spectator character
                return(true);
            }

            // check that there are no other objects on the way between them (defined by default layer)
            var physicsSpace    = character.PhysicsBody.PhysicsSpace;
            var characterCenter = character.Position + character.PhysicsBody.CenterOffset;

            if (!ObstacleTestHelper.SharedHasObstaclesOnTheWay(characterCenter,
                                                               physicsSpace,
                                                               worldObject,
                                                               sendDebugEvents: writeToLog))
            {
                return(true);
            }

            if (writeToLog)
            {
                Logger.Warning(
                    $"Character cannot interact with {worldObject} - there are other objects on the way.",
                    character);

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

            return(false);
        }
Exemplo n.º 27
0
        public bool ServerGather(IStaticWorldObject worldObject, ICharacter character)
        {
            var privateState = GetPrivateState(worldObject);

            if (privateState.IsDropListSpawned)
            {
                // this loot container was already search - drop list was already spawned
                return(true);
            }

            // spawn items accordingly to the droplist
            privateState.IsDropListSpawned = true;

            var lootDroplist    = this.ServerGetLootDroplist(worldObject);
            var dropItemContext = new DropItemContext(character, worldObject);
            CreateItemResult dropItemResult;
            var attemptRemains = 100;
            var itemsContainer = privateState.ItemsContainer;

            do
            {
                dropItemResult = lootDroplist.TryDropToContainer(itemsContainer, dropItemContext);
            }
            // ensure that at least something is spawned...
            // perhaps that's not a good idea, but we have an attempts limit
            while (dropItemResult.TotalCreatedCount == 0 &&
                   --attemptRemains > 0);

            Server.Items.SetSlotsCount(itemsContainer, (byte)itemsContainer.OccupiedSlotsCount);

            character.ServerAddSkillExperience <SkillSearching>(SkillSearching.ExperienceAddWhenSearching);

            Server.World.EnterPrivateScope(character, worldObject);

            // register private scope exit on interaction cancel
            InteractionCheckerSystem.SharedRegister(
                character,
                worldObject,
                finishAction: isAbort =>
            {
                if (worldObject.IsDestroyed)
                {
                    return;
                }

                Server.World.ExitPrivateScope(character, worldObject);

                if (isAbort)
                {
                    // notify client
                    this.CallClient(character, _ => _.ClientRemote_FinishInteraction(worldObject));
                }

                if (this.IsAutoDestroyWhenLooted)
                {
                    // container was closed - destroy it
                    Server.World.DestroyObject(worldObject);
                }
            });

            Logger.Important($"Started object interaction with {worldObject} for {character}");
            this.CallClient(character, _ => _.ClientRemote_OnContainerOpened(worldObject));
            return(true);
        }
Exemplo n.º 28
0
        public bool ServerGather(IStaticWorldObject worldObject, ICharacter character)
        {
            var privateState = GetPrivateState(worldObject);

            if (privateState.IsDropListSpawned)
            {
                // this loot container was already search - drop list was already spawned
                return(true);
            }

            // spawn items accordingly to the droplist
            privateState.IsDropListSpawned = true;
            var skillExperienceToAdd = SkillSearching.ExperienceAddWhenSearching
                                       * this.SearchingSkillExperienceMultiplier;

            var lootDroplist    = this.ServerGetLootDroplist(worldObject);
            var dropItemContext = new DropItemContext(character, worldObject);

            CreateItemResult dropItemResult;

            if (this.IsAutoTakeAll)
            {
                // try to simply pickup the content
                dropItemResult = lootDroplist.TryDropToCharacter(character,
                                                                 dropItemContext,
                                                                 sendNoFreeSpaceNotification: false);
                if (dropItemResult.IsEverythingCreated &&
                    dropItemResult.TotalCreatedCount > 0)
                {
                    NotificationSystem.ServerSendItemsNotification(character, dropItemResult);
                    Server.World.DestroyObject(worldObject); // destroy object after success pickup
                    character.ServerAddSkillExperience <SkillSearching>(skillExperienceToAdd);
                    ServerLootEventHelper.OnLootReceived(character, worldObject);
                    return(true);
                }

                dropItemResult.Rollback();
            }

            // create a container and drop items there
            var attemptRemains = 100;
            var itemsContainer = privateState.ItemsContainer;

            do
            {
                dropItemResult = lootDroplist.TryDropToContainer(itemsContainer, dropItemContext);
            }
            // ensure that at least something is spawned...
            // perhaps that's not a good idea, but we have an attempts limit
            while (dropItemResult.TotalCreatedCount == 0 &&
                   --attemptRemains > 0);

            Server.Items.SetSlotsCount(itemsContainer, itemsContainer.OccupiedSlotsCount);

            character.ServerAddSkillExperience <SkillSearching>(skillExperienceToAdd);
            ServerLootEventHelper.OnLootReceived(character, worldObject);

            Server.World.EnterPrivateScope(character, worldObject);

            // register private scope exit on interaction cancel
            InteractionCheckerSystem.SharedRegister(
                character,
                worldObject,
                finishAction: isAbort =>
            {
                if (worldObject.IsDestroyed)
                {
                    return;
                }

                Server.World.ExitPrivateScope(character, worldObject);

                if (isAbort)
                {
                    // notify client
                    this.CallClient(character, _ => _.ClientRemote_FinishInteraction(worldObject));
                }

                if (this.IsAutoDestroyWhenLooted)
                {
                    // container was closed - destroy it
                    Server.World.DestroyObject(worldObject);
                }
            });

            Logger.Important($"Started object interaction with {worldObject} for {character}");
            this.CallClient(character, _ => _.ClientRemote_OnContainerOpened(worldObject));
            return(true);
        }