public override bool SharedCanInteract(ICharacter character, IStaticWorldObject worldObject, bool writeToLog)
        {
            if (!base.SharedCanInteract(character, worldObject, writeToLog))
            {
                return(false);
            }

            if (GetPublicState(worldObject).OwnerName == character.Name)
            {
                return(true);
            }

            if (NewbieProtectionSystem.SharedIsNewbie(character))
            {
                // newbie cannot pickup other players' loot
                if (writeToLog)
                {
                    NewbieProtectionSystem.SharedShowNewbieCannotDamageOtherPlayersOrLootBags(character,
                                                                                              isLootBag: true);
                }

                return(false);
            }

            // non-newbie character can pickup players' loot
            // please note this validation has an override for derived ObjectPlayerLootContainerProtected
            return(true);
        }
Пример #2
0
        public override bool SharedCanInteract(ICharacter character, IStaticWorldObject worldObject, bool writeToLog)
        {
            if (!base.SharedCanInteract(character, worldObject, writeToLog))
            {
                return(false);
            }

            var ownerName = GetPublicState(worldObject).OwnerName;

            if (ownerName == character.Name)
            {
                return(true);
            }

            if (PveSystem.SharedIsPve(false))
            {
                if (IsClient && PartySystem.ClientIsPartyMember(ownerName) ||
                    (IsServer &&
                     PartySystem.ServerIsSameParty(Server.Characters.GetPlayerCharacter(ownerName),
                                                   character)))
                {
                    // in PvE party members can pickup items of their party members
                }
                else
                {
                    // other players in PvE cannot pickup player's loot
                    if (writeToLog && IsClient)
                    {
                        PveSystem.ClientShowNotificationActionForbidden();
                    }

                    return(false);
                }
            }

            if (NewbieProtectionSystem.SharedIsNewbie(character))
            {
                // newbie cannot pickup other players' loot
                if (writeToLog)
                {
                    NewbieProtectionSystem.SharedShowNewbieCannotDamageOtherPlayersOrLootBags(character,
                                                                                              isLootBag: true);
                }

                return(false);
            }

            // non-newbie character can pickup players' loot
            // please note this validation has an override for derived ObjectPlayerLootContainerProtected
            return(true);
        }
Пример #3
0
        protected override double SharedCalculateDamageByWeapon(
            WeaponFinalCache weaponCache,
            double damagePreMultiplier,
            IStaticWorldObject targetObject,
            out double obstacleBlockDamageCoef)
        {
            var serverTime = IsServer
                                 ? Server.Game.FrameTime
                                 : Client.CurrentGame.ServerFrameTimeApproximated;

            if (serverTime < GetPublicState(targetObject).CooldownUntilServerTime)
            {
                // too hot for mining - no damage to it
                if (IsClient &&
                    weaponCache.ProtoWeapon is IProtoItemToolMining)
                {
                    NotificationSystem.ClientShowNotification(CoreStrings.Meteorite_CooldownMessage_TooHotForMining,
                                                              color: NotificationColor.Bad,
                                                              icon: this.Icon);
                }

                if (IsServer &&
                    weaponCache.ProtoWeapon is IProtoItemWeaponMelee &&
                    !weaponCache.Character.IsNpc)
                {
                    weaponCache.Character.ServerAddStatusEffect <StatusEffectHeat>(intensity: 0.5);
                }

                obstacleBlockDamageCoef = this.ObstacleBlockDamageCoef;
                return(0);
            }

            // meteorite cooldown finished
            if (NewbieProtectionSystem.SharedIsNewbie(weaponCache.Character))
            {
                // don't allow mining meteorite while under newbie protection
                if (IsClient)
                {
                    NewbieProtectionSystem.ClientNotifyNewbieCannotPerformAction(this);
                }

                obstacleBlockDamageCoef = 0;
                return(0);
            }

            return(base.SharedCalculateDamageByWeapon(weaponCache,
                                                      damagePreMultiplier,
                                                      targetObject,
                                                      out obstacleBlockDamageCoef));
        }
        public string Execute(bool isEnabled, [CurrentCharacterIfNull] ICharacter character)
        {
            if (isEnabled)
            {
                NewbieProtectionSystem.ServerRegisterNewbie(character);
            }
            else
            {
                NewbieProtectionSystem.ServerDisableNewbieProtection(character);
            }

            return(character
                   + " is now "
                   + (NewbieProtectionSystem.SharedIsNewbie(character)
                          ? "under newbie protection."
                          : "without newbie protection."));
        }
Пример #5
0
        public override bool SharedCanInteract(ICharacter character, IStaticWorldObject worldObject, bool writeToLog)
        {
            if (!base.SharedCanInteract(character, worldObject, writeToLog))
            {
                return(false);
            }

            if (NewbieProtectionSystem.SharedIsNewbie(character))
            {
                // don't allow hacking while under newbie protection
                if (writeToLog && IsClient)
                {
                    NewbieProtectionSystem.ClientNotifyNewbieCannotPerformAction(this);
                }

                return(false);
            }

            return(true);
        }
Пример #6
0
        private static IItemsContainer ServerTryCreateLootContainerAtPosition(
            Vector2Ushort tilePosition,
            ICharacter character,
            bool writeWarningsToLog = true)
        {
            IStaticWorldObject objectLootContainer = null;
            var protoLootContainer = NewbieProtectionSystem.SharedIsNewbie(character)
                                         ? Api.GetProtoEntity <ObjectPlayerLootContainerProtected>()
                                         : Api.GetProtoEntity <ObjectPlayerLootContainer>();

            if (protoLootContainer.CheckTileRequirements(tilePosition, character: null, logErrors: false))
            {
                Logger.Important("Creating loot container at " + tilePosition);
                objectLootContainer = Server.World.CreateStaticWorldObject(protoLootContainer, tilePosition);
            }

            if (objectLootContainer is null)
            {
                // cannot create loot container
                if (writeWarningsToLog)
                {
                    Logger.Warning(
                        $"Cannot create loot container at {tilePosition} - tile contains something preventing it.");
                }

                return(null);
            }

            var lootPrivateState = GetPrivateState(objectLootContainer);

            lootPrivateState.Owner = character;
            ServerSetDefaultDestroyTimeout(lootPrivateState);
            GetPublicState(objectLootContainer).OwnerName = character.Name;

            var characterPrivateState = PlayerCharacter.GetPrivateState(character);

            characterPrivateState.DroppedLootLocations.Add(
                new DroppedLootInfo(tilePosition, lootPrivateState.DestroyAtTime));

            return(lootPrivateState.ItemsContainer);
        }
Пример #7
0
        protected override double SharedCalculateDamageByWeapon(
            WeaponFinalCache weaponCache,
            double damagePreMultiplier,
            IStaticWorldObject targetObject,
            out double obstacleBlockDamageCoef)
        {
            if (NewbieProtectionSystem.SharedIsNewbie(weaponCache.Character))
            {
                // don't allow mining a boss loot while under newbie protection
                if (IsClient)
                {
                    NewbieProtectionSystem.ClientNotifyNewbieCannotPerformAction(this);
                }

                obstacleBlockDamageCoef = 0;
                return(0);
            }

            return(base.SharedCalculateDamageByWeapon(weaponCache,
                                                      damagePreMultiplier,
                                                      targetObject,
                                                      out obstacleBlockDamageCoef));
        }
Пример #8
0
        public void SharedValidatePlacement(
            ICharacter character,
            Vector2Ushort targetPosition,
            bool logErrors,
            out bool canPlace,
            out bool isTooFar)
        {
            if (NewbieProtectionSystem.SharedIsNewbie(character))
            {
                if (logErrors)
                {
                    Logger.Warning("Newbie cannot plant bombs");
                    NewbieProtectionSystem.SharedNotifyNewbieCannotPerformAction(character, this);
                }

                canPlace = false;
                isTooFar = false;
                return;
            }

            // check if there is a direct line of sight
            // 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 = targetPosition.ToVector2D() + (0.5, 0.5);

            // 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: false);
                foreach (var test in obstaclesOnTheWay.AsList())
                {
                    var testPhysicsBody = test.PhysicsBody;
                    if (testPhysicsBody.AssociatedProtoTile != null)
                    {
                        // obstacle tile on the way
                        return(true);
                    }

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

                    switch (testWorldObject.ProtoWorldObject)
                    {
                    case IProtoObjectDeposit _:     // allow deposits
                    case ObjectWallDestroyed _:     // allow destroyed walls
                        continue;
                    }

                    // obstacle object on the way
                    return(true);
                }

                // no obstacles
                return(false);
            }

            // let's test by casting rays from character center to the center of the planted bomb
            if (TestHasObstacle(worldObjectCenter))
            {
                // has obstacle
                if (logErrors)
                {
                    if (IsClient)
                    {
                        this.ClientShowCannotPlaceObstaclesOnTheWayNotification();
                    }
                    else
                    {
                        Logger.Warning($"{character} cannot place {this} - obstacles on the way");
                        this.CallClient(character, _ => _.ClientRemote_CannotPlaceObstacles());
                    }
                }

                canPlace = false;
                isTooFar = false;
                return;
            }

            if (!this.ObjectExplosiveProto.CheckTileRequirements(targetPosition,
                                                                 character,
                                                                 logErrors))
            {
                // explosive static object placement requirements failed
                canPlace = false;
                isTooFar = false;
                return;
            }

            // validate distance to the character
            if (this.SharedIsTooFarToPlace(character, targetPosition, logErrors))
            {
                canPlace = true;
                isTooFar = true;
                return;
            }

            canPlace = true;
            isTooFar = false;
        }
Пример #9
0
        public void SharedValidatePlacement(
            ICharacter character,
            Vector2Ushort targetPosition,
            bool logErrors,
            out bool canPlace,
            out bool isTooFar,
            out object errorCodeOrMessage)
        {
            if (NewbieProtectionSystem.SharedIsNewbie(character))
            {
                if (logErrors)
                {
                    NewbieProtectionSystem.SharedNotifyNewbieCannotPerformAction(character, this);
                }

                canPlace           = false;
                isTooFar           = false;
                errorCodeOrMessage = null;
                return;
            }

            // check whether somebody nearby is already placing a bomb there
            var tempCharactersNearby = Api.Shared.GetTempList <ICharacter>();

            if (IsServer)
            {
                Server.World.GetScopedByPlayers(character, tempCharactersNearby);
            }
            else
            {
                Client.Characters.GetKnownPlayerCharacters(tempCharactersNearby);
            }

            foreach (var otherCharacter in tempCharactersNearby.AsList())
            {
                if (otherCharacter != character &&
                    otherCharacter.IsInitialized &&
                    PlayerCharacter.GetPublicState(otherCharacter).CurrentPublicActionState
                    is ItemExplosiveActionPublicState explosiveActionState &&
                    explosiveActionState.TargetPosition == targetPosition)
                {
                    // someone is already planting a bomb here
                    canPlace           = false;
                    isTooFar           = false;
                    errorCodeOrMessage = null;
                    return;
                }
            }

            // check if there is a direct line of sight
            // 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 = targetPosition.ToVector2D() + (0.5, 0.5);

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

                    var testWorldObject = testPhysicsBody.AssociatedWorldObject;
                    if (testWorldObject is null)
                    {
                        // some barrier on the way
                        return(true);
                    }

                    if (testWorldObject == character)
                    {
                        // not an obstacle - it's the character or world object itself
                        continue;
                    }

                    switch (testWorldObject.ProtoWorldObject)
                    {
                    case IProtoObjectDeposit:     // allow deposits
                    case ObjectWallDestroyed:     // allow destroyed walls
                        continue;
                    }

                    // obstacle object on the way
                    return(true);
                }

                // no obstacles
                return(false);
            }

            if (!this.ObjectExplosiveProto.CheckTileRequirements(targetPosition,
                                                                 character,
                                                                 out errorCodeOrMessage,
                                                                 logErrors))
            {
                // explosive static object placement requirements failed
                canPlace = false;
                isTooFar = false;
                return;
            }

            // let's check whether there are any obstacles by casting rays
            // from character's center to the center of the planted bomb
            if (TestHasObstacle(worldObjectCenter))
            {
                // has obstacle
                if (logErrors)
                {
                    if (IsClient)
                    {
                        this.ClientShowCannotPlaceObstaclesInTheWayNotification();
                    }
                    else
                    {
                        Logger.Warning($"{character} cannot place {this} - obstacles in the way");
                        this.CallClient(character, _ => _.ClientRemote_CannotPlaceObstacles());
                    }
                }

                canPlace           = false;
                isTooFar           = false;
                errorCodeOrMessage = CoreStrings.Notification_ObstaclesOnTheWay;
                return;
            }

            // validate distance to the character
            if (this.SharedIsTooFarToPlace(character, targetPosition, logErrors))
            {
                canPlace = true;
                isTooFar = true;
                return;
            }

            canPlace = true;
            isTooFar = false;
        }
Пример #10
0
        public override bool SharedOnDamage(
            WeaponFinalCache weaponCache,
            IWorldObject targetObject,
            double damagePreMultiplier,
            double damagePostMultiplier,
            out double obstacleBlockDamageCoef,
            out double damageApplied)
        {
            var byCharacter = weaponCache.Character;

            if (NewbieProtectionSystem.SharedIsNewbie(byCharacter))
            {
                // don't allow attacking a boss while under newbie protection
                if (IsClient)
                {
                    NewbieProtectionSystem.ClientNotifyNewbieCannotPerformAction(this);
                }

                obstacleBlockDamageCoef = 0;
                damageApplied           = 0;
                return(false);
            }

            if (IsServer)
            {
                // apply the difficulty coefficient
                damagePostMultiplier /= ServerBossDifficultyCoef;

                if (weaponCache.ProtoExplosive != null)
                {
                    // the boss is massive so it will take x2 damage from explosives (such as grenades)
                    damagePostMultiplier *= 2;
                }
            }

            var result = base.SharedOnDamage(weaponCache,
                                             targetObject,
                                             damagePreMultiplier,
                                             damagePostMultiplier,
                                             out obstacleBlockDamageCoef,
                                             out damageApplied);

            if (IsServer &&
                result &&
                byCharacter != null &&
                !byCharacter.IsNpc)
            {
                var privateState = GetPrivateState((ICharacter)targetObject);

                // record the damage dealt by player
                privateState.DamageTracker.RegisterDamage(byCharacter, damageApplied);

                if (damageApplied > 1 / ServerBossDifficultyCoef)
                {
                    // record the last time a significant damage is dealt
                    privateState.LastDamageTime = Server.Game.FrameTime;
                }
            }

            return(result);
        }
Пример #11
0
        private static void ServerCharacterDeathHandler(ICharacter deadCharacter)
        {
            if (deadCharacter.IsNpc)
            {
                return;
            }

            var statistics = PlayerCharacter.GetPrivateState(deadCharacter).Statistics;

            statistics.Deaths++;
            Logger.Important($"{deadCharacter} died. Total deaths: {statistics.Deaths}");

            if (PveSystem.ServerIsPvE)
            {
                return;
            }

            using var tempKilledBy = ServerGetKilledByPlayerCharacters(deadCharacter);
            if (tempKilledBy is null ||
                tempKilledBy.Count == 0)
            {
                return;
            }

            // increase kills counter only for the first killer (with the highest damage ratio)
            IncreasePvpKillsCounter(killer: tempKilledBy.AsList()[0].Killer,
                                    deadCharacter);

            if (NewbieProtectionSystem.SharedIsNewbie(deadCharacter))
            {
                Logger.Important(
                    $"{deadCharacter} was killed while under newbie protection, PvP score mechanic doesn't apply");
                return;
            }

            var originalPvpScore = statistics.PvpScore;

            if (originalPvpScore <= 0)
            {
                // cannot take PvP score
                Logger.Important($"{deadCharacter} was killed but has PvP score 0 so no score is lost");
                return;
            }

            double pvpScoreTaken;

            if (originalPvpScore <= 1)
            {
                pvpScoreTaken = 1;
            }
            else
            {
                pvpScoreTaken = MathHelper.Clamp(Math.Floor(originalPvpScore * PvpScoreLossOnDeathFraction),
                                                 min: 1,
                                                 max: originalPvpScore);
            }

            statistics.PvpScore = Math.Max(0, originalPvpScore - pvpScoreTaken);
            if (statistics.PvpScore == 0)
            {
                statistics.ServerPvpScoreNextRecoveryTime = Server.Game.FrameTime + PvpScoreRestoreDuration;
            }

            Logger.Important(
                string.Format(
                    "{0} was killed in PvP and lost PvP score: {1}Previous score: {2:0.##}{1}New score: {3:0.##}",
                    deadCharacter,
                    Environment.NewLine,
                    originalPvpScore,
                    statistics.PvpScore));

            ServerDistributePvpScore(tempKilledBy.AsList(), pvpScoreTaken, deadCharacter);
        }
Пример #12
0
        public static bool SharedOnDamageToCharacter(
            ICharacter targetCharacter,
            WeaponFinalCache weaponCache,
            double damageMultiplier,
            out double damageApplied)
        {
            var targetPublicState  = targetCharacter.GetPublicState <ICharacterPublicState>();
            var targetCurrentStats = targetPublicState.CurrentStats;

            if (targetCurrentStats.HealthCurrent <= 0)
            {
                // target character is dead, cannot apply damage to it
                damageApplied = 0;
                return(false);
            }

            {
                if (!targetCharacter.IsNpc &&
                    weaponCache.Character is ICharacter damagingCharacter &&
                    NewbieProtectionSystem.SharedIsNewbie(damagingCharacter))
                {
                    // no damage from newbie
                    damageApplied = 0;
                    if (Api.IsClient)
                    {
                        // display message to newbie
                        NewbieProtectionSystem.ClientShowNewbieCannotDamageOtherPlayersOrLootBags(isLootBag: false);
                    }

                    // but the hit is registered so it's not possible to shoot through a character
                    return(true);
                }
            }

            if (Api.IsClient)
            {
                // we don't simulate the damage on the client side
                damageApplied = 0;

                if (weaponCache.Character is ICharacter damagingCharacter)
                {
                    // potentially a PvP case
                    PveSystem.ClientShowDuelModeRequiredNotificationIfNecessary(
                        damagingCharacter,
                        targetCharacter);
                }

                return(true);
            }

            var attackerCharacter = weaponCache.Character;

            if (!(attackerCharacter is null) &&
                attackerCharacter.IsNpc &&
                targetCharacter.IsNpc)
            {
                // no creature-to-creature damage
                damageApplied = 0;
                return(false);
            }

            // calculate and apply damage on server
            var targetFinalStatsCache =
                targetCharacter.GetPrivateState <BaseCharacterPrivateState>()
                .FinalStatsCache;

            var totalDamage = ServerCalculateTotalDamage(
                weaponCache,
                targetCharacter,
                targetFinalStatsCache,
                damageMultiplier,
                clampDefenseTo1: true);

            if (totalDamage <= 0)
            {
                // damage suppressed
                damageApplied = 0;
                return(true);
            }

            // Clamp the max receivable damage to x5 from the max health.
            // This will help in case when the too much damage is dealt (mega-bomb!)
            // to ensure the equipment will not receive excessive damaged.
            totalDamage = Math.Min(totalDamage, 5 * targetCurrentStats.HealthMax);

            // apply damage
            if (!(attackerCharacter is null))
            {
                targetCurrentStats.ServerReduceHealth(totalDamage, damageSource: attackerCharacter);
            }