コード例 #1
0
        public static void OnCharacterDeath(ICharacter deadCharacter)
        {
            var publicState = deadCharacter.GetPublicState <ICharacterPublicState>();

            if (!publicState.IsDead)
            {
                publicState.CurrentStats.ServerSetHealthCurrent(0);
                return;
            }

            // recreate physics (as dead character doesn't have any physics)
            deadCharacter.ProtoCharacter.SharedCreatePhysics(deadCharacter);

            Action <ICharacter> onCharacterDeath;

            if (deadCharacter.ProtoCharacter is IProtoCharacterMob protoCharacterMob)
            {
                onCharacterDeath = CharacterDeath;
                if (onCharacterDeath is not null)
                {
                    Api.SafeInvoke(() => onCharacterDeath(deadCharacter));
                }

                protoCharacterMob.ServerOnDeath(deadCharacter);
                return;
            }

            // player character death
            // remember the death position (useful for the respawn)
            var privateState = PlayerCharacter.GetPrivateState(deadCharacter);

            privateState.LastDeathPosition = deadCharacter.TilePosition;
            privateState.LastDeathTime     = Api.Server.Game.FrameTime;
            ServerTimersSystem.AddAction(delaySeconds: PlayerTeleportToGraveyardDelaySeconds,
                                         () => TeleportDeadPlayerCharacterToGraveyard(deadCharacter));

            var isPvPdeath = CharacterDamageTrackingSystem.ServerGetPvPdamagePercent(deadCharacter)
                             >= 0.5;

            // register death (required even if the player is not a newbie)
            NewbieProtectionSystem.ServerRegisterDeath(deadCharacter,
                                                       isPvPdeath,
                                                       out var shouldSufferDeathConsequences);

            if (shouldSufferDeathConsequences)
            {
                DropPlayerLoot(deadCharacter);
            }
            else
            {
                Api.Logger.Important("Player character is dead - newbie PvP case, no loot drop or other consequences",
                                     deadCharacter);
            }

            onCharacterDeath = CharacterDeath;
            if (onCharacterDeath is not null)
            {
                Api.SafeInvoke(() => onCharacterDeath(deadCharacter));
            }
        }
コード例 #2
0
        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);
        }
コード例 #3
0
        public static async void ClientInvitationAccept(string clanTag)
        {
            if (NewbieProtectionSystem.ClientIsNewbie)
            {
                NewbieProtectionSystem.ClientNotifyNewbieCannotPerformAction(null);
                return;
            }

            if (ClientCheckIsUnderJoinCooldown(showErrorNotification: true))
            {
                return;
            }

            var timeRemains = await Instance.CallServer(
                _ => _.ServerRemote_GetCooldownRemainsToJoinReturnToFaction(clanTag));

            if (timeRemains > 0)
            {
                var factionEmblem =
                    await ClientFactionEmblemTextureProvider.GetEmblemTextureAsync(clanTag, useCache : true);

                NotificationSystem.ClientShowNotification(
                    title: CoreStrings.Faction_ErrorUnderJoinCooldown,
                    // TODO: consider using a separate text constant here
                    message: string.Format(CoreStrings.ShieldProtection_CooldownRemains_Format,
                                           ClientTimeFormatHelper.FormatTimeDuration(timeRemains)),
                    NotificationColor.Bad,
                    icon: factionEmblem);
                return;
            }

            DialogWindow.ShowDialog(
                title: CoreStrings.Faction_Join,
                text: string.Format(CoreStrings.Faction_DialogJoinConfirmation_Message_Format,
                                    @$ "\[{clanTag}\]")
コード例 #4
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);
        }
コード例 #5
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));
        }
コード例 #6
0
        public override bool SharedCanInteract(ICharacter character, IStaticWorldObject worldObject, bool writeToLog)
        {
            // don't use the base implementation as it will not work in PvE
            // (action forbidden if player doesn't have access to the land claim)
            if (character.GetPublicState <ICharacterPublicState>().IsDead)
            {
                return(false);
            }

            if (!NewbieProtectionSystem.SharedValidateInteractionIsNotForbidden(character, worldObject, writeToLog))
            {
                return(false);
            }

            return(this.SharedIsInsideCharacterInteractionArea(character, worldObject, writeToLog));
        }
コード例 #7
0
        public static void ClientApplicantSubmitApplication(string clanTag)
        {
            if (NewbieProtectionSystem.ClientIsNewbie)
            {
                NewbieProtectionSystem.ClientNotifyNewbieCannotPerformAction(null);
                return;
            }

            if (ClientCheckIsUnderJoinCooldown(showErrorNotification: true))
            {
                return;
            }

            DialogWindow.ShowDialog(
                title: CoreStrings.Faction_SubmitApplication,
                string.Format(CoreStrings.Faction_DialogSubmitApplication_Message_Format,
                              @$ "\[{clanTag}\]")
コード例 #8
0
        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."));
        }
コード例 #9
0
        protected override void ServerInitializeCharacterFirstTime(ServerInitializeData data)
        {
            base.ServerInitializeCharacterFirstTime(data);

            var character   = data.GameObject;
            var publicState = data.PublicState;

            if (!Api.IsEditor)
            {
                NewbieProtectionSystem.ServerRegisterNewbie(character);

                publicState.IsMale    = 1 == RandomHelper.Next(0, maxValueExclusive: 2); // male/female ratio: 50/50
                publicState.FaceStyle = SharedCharacterFaceStylesProvider
                                        .GetForGender(publicState.IsMale)
                                        .GenerateRandomFace();
            }
            else // if Editor
            {
                publicState.IsMale    = true;
                publicState.FaceStyle = SharedCharacterFaceStylesProvider
                                        .GetForGender(publicState.IsMale)
                                        .GetDefaultFaceInEditor();
            }

            ServerPlayerSpawnManager.ServerAddTorchItemIfNoItems(character);

            if (!Api.IsEditor)
            {
                return;
            }

            // the game is run as Editor
            // auto pwn in editor mode
            ConsoleCommandsSystem.SharedGetCommand <ConsoleAdminPwn>().Execute(player: character);
            // add all the skills
            ConsoleCommandsSystem.SharedGetCommand <ConsoleSkillsSetAll>().Execute(player: character);
            // add all the technologies
            ConsoleCommandsSystem.SharedGetCommand <ConsoleTechAddAll>().Execute(player: character);

            this.ServerRebuildFinalCacheIfNeeded(data.PrivateState, publicState);

            // add all the quests (and complete them)
            ConsoleCommandsSystem.SharedGetCommand <ConsoleQuestCompleteAll>().Execute(player: character);
        }
コード例 #10
0
        public virtual bool SharedCanInteract(ICharacter character, TWorldObject worldObject, bool writeToLog)
        {
            if (!this.IsInteractableObject)
            {
                return(false);
            }

            if (character.GetPublicState <ICharacterPublicState>().IsDead ||
                IsServer && !character.ServerIsOnline)
            {
                return(false);
            }

            if (worldObject is IStaticWorldObject staticWorldObject)
            {
                if (PveSystem.SharedIsPve(clientLogErrorIfDataIsNotYetAvailable: false))
                {
                    if (!PveSystem.SharedValidateInteractionIsNotForbidden(character,
                                                                           staticWorldObject,
                                                                           writeToLog))
                    {
                        // action forbidden by PvE system
                        return(false);
                    }
                }
                else // PvP servers have newbie protection system
                {
                    if (!NewbieProtectionSystem.SharedValidateInteractionIsNotForbidden(
                            character,
                            staticWorldObject,
                            writeToLog))
                    {
                        // action forbidden by newbie protection system
                        return(false);
                    }
                }
            }

            return(this.SharedIsInsideCharacterInteractionArea(character,
                                                               worldObject,
                                                               writeToLog));
        }
コード例 #11
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);
        }
コード例 #12
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);
        }
コード例 #13
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));
        }
コード例 #14
0
        protected override void ServerInitializeCharacterFirstTime(ServerInitializeData data)
        {
            base.ServerInitializeCharacterFirstTime(data);

            var character   = data.GameObject;
            var publicState = data.PublicState;

            if (!Api.IsEditor)
            {
                NewbieProtectionSystem.ServerRegisterNewbie(character);
            }

            publicState.IsMale    = true;
            publicState.FaceStyle = SharedCharacterFaceStylesProvider
                                    .GetForGender(publicState.IsMale)
                                    .GenerateRandomFace();

            ServerPlayerSpawnManager.ServerAddTorchItemIfNoItems(character);

            if (!Api.IsEditor)
            {
                return;
            }

            // the game is run as Editor
            // auto pwn in editor mode
            GetProtoEntity <ConsoleAdminPwn>().Execute(player: character);
            // add all the skills
            GetProtoEntity <ConsoleSkillsSetAll>().Execute(player: character);
            // add all the technologies
            GetProtoEntity <ConsoleTechAddAll>().Execute(player: character);

            this.ServerRebuildFinalCacheIfNeeded(data.PrivateState, publicState);

            // add all the quests (and complete them)
            GetProtoEntity <ConsoleQuestCompleteAll>().Execute(player: character);
        }
コード例 #15
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);
        }
コード例 #16
0
        private static void ServerAddMember(
            ICharacter character,
            ILogicObject faction,
            FactionMemberRole role)
        {
            Api.Assert(!character.IsNpc, "NPC cannot join a faction");

            if (!SharedIsValidRole(role))
            {
                throw new Exception("Invalid role: " + role);
            }

            var currentFaction = ServerGetFaction(character);

            if (currentFaction == faction)
            {
                // already in faction
                return;
            }

            if (currentFaction is not null)
            {
                throw new Exception($"Player already has a faction: {character} in {faction}");
            }

            // faction members cannot have a newbie protection
            NewbieProtectionSystem.ServerDisableNewbieProtection(character);

            var members            = ServerGetFactionMembersEditable(faction);
            var factionPublicState = Faction.GetPublicState(faction);
            var maxMembers         = FactionConstants.SharedGetFactionMembersMax(factionPublicState.Kind);

            if (members.Count >= maxMembers)
            {
                throw new Exception("Faction size exceeded - max " + maxMembers);
            }

            if (role == FactionMemberRole.Leader)
            {
                foreach (var otherMember in members)
                {
                    if (otherMember.Role == FactionMemberRole.Leader)
                    {
                        throw new Exception("Faction can have only a single leader");
                    }
                }
            }

            members.Add(new FactionMemberEntry(character.Name, role));
            ServerCharacterFactionDictionary[character]       = faction;
            PlayerCharacter.GetPublicState(character).ClanTag = factionPublicState.ClanTag;
            factionPublicState.PlayersNumberCurrent++;

            Logger.Important($"Player joined faction: {character} in {faction} - role: {role}",
                             character);

            ServerInvitations.RemoveAllInvitationsFor(character);

            Api.SafeInvoke(
                () => ServerCharacterJoinedOrLeftFaction?.Invoke(character,
                                                                 faction,
                                                                 isJoined: true));

            // add this with some delay to prevent from the bug when the player name listed twice due to the late delta-replication
            ServerTimersSystem.AddAction(delaySeconds: 0.1,
                                         () => ServerSendCurrentFaction(character));
        }
コード例 #17
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;
        }
コード例 #18
0
        public virtual bool SharedCanInteract(ICharacter character, TWorldObject worldObject, bool writeToLog)
        {
            if (!this.IsInteractableObject)
            {
                return(false);
            }

            try
            {
                this.VerifyGameObject(worldObject);
            }
            catch (Exception ex)
            {
                Logger.Warning($"Interaction check failed: {ex.GetType().FullName}: {ex.Message}");
                return(false);
            }

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

                return(false);
            }

            if (worldObject is IStaticWorldObject staticWorldObject)
            {
                if (PveSystem.SharedIsPve(clientLogErrorIfDataIsNotYetAvailable: false))
                {
                    if (!PveSystem.SharedValidateInteractionIsNotForbidden(character,
                                                                           staticWorldObject,
                                                                           writeToLog))
                    {
                        // action forbidden by PvE system
                        return(false);
                    }
                }
                else // PvP servers have newbie protection system
                {
                    if (!NewbieProtectionSystem.SharedValidateInteractionIsNotForbidden(
                            character,
                            staticWorldObject,
                            writeToLog))
                    {
                        // action forbidden by newbie protection system
                        return(false);
                    }
                }
            }

            return(this.SharedIsInsideCharacterInteractionArea(character,
                                                               worldObject,
                                                               writeToLog));
        }
コード例 #19
0
        public virtual bool SharedOnDamage(
            WeaponFinalCache weaponCache,
            IStaticWorldObject targetObject,
            double damagePreMultiplier,
            out double obstacleBlockDamageCoef,
            out double damageApplied)
        {
            var objectPublicState       = GetPublicState(targetObject);
            var previousStructurePoints = objectPublicState.StructurePointsCurrent;

            if (previousStructurePoints <= 0f)
            {
                // already destroyed static world object
                obstacleBlockDamageCoef = 0;
                damageApplied           = 0;
                return(false);
            }

            var serverDamage = this.SharedCalculateDamageByWeapon(
                weaponCache,
                damagePreMultiplier,
                targetObject,
                out obstacleBlockDamageCoef);

            if (serverDamage < 0)
            {
                Logger.Warning(
                    $"Server damage less than 0 and this is suspicious. {this} calculated damage: {serverDamage:0.###}");
                serverDamage = 0;
            }

            if (IsClient)
            {
                // simply call these methods to display a client notification only!
                // they are not used for anything else here
                // to calculate damage they're used in WeaponDamageSystem.ServerCalculateTotalDamage method.
                RaidingProtectionSystem.SharedCanRaid(targetObject,
                                                      showClientNotification: true);
                LandClaimShieldProtectionSystem.SharedCanRaid(targetObject,
                                                              showClientNotification: true);
                PveSystem.SharedIsAllowStaticObjectDamage(weaponCache.Character,
                                                          targetObject,
                                                          showClientNotification: true);
                NewbieProtectionSystem.SharedIsAllowStructureDamage(weaponCache.Character,
                                                                    targetObject,
                                                                    showClientNotification: true);

                damageApplied = 0;
                return(true);
            }

            if (serverDamage <= 0)
            {
                // no damage applied
                damageApplied = 0;
                return(true);
            }

            // apply damage
            damageApplied = serverDamage;
            var newStructurePoints = (float)(previousStructurePoints - serverDamage);

            if (newStructurePoints < 0)
            {
                newStructurePoints = 0;
            }

            Logger.Info(
                $"Damage applied to {targetObject} by {weaponCache.Character}:\n{serverDamage} dmg, current structure points {newStructurePoints}/{this.StructurePointsMax}, {weaponCache.Weapon}");

            objectPublicState.StructurePointsCurrent = newStructurePoints;

            try
            {
                this.ServerOnStaticObjectDamageApplied(
                    weaponCache,
                    targetObject,
                    previousStructurePoints,
                    newStructurePoints);
            }
            catch (Exception ex)
            {
                Logger.Exception(ex, $"Problem on processing {nameof(this.ServerOnStaticObjectDamageApplied)}()");
            }

            if (newStructurePoints <= 0f)
            {
                this.ServerOnStaticObjectZeroStructurePoints(weaponCache, weaponCache.Character, targetObject);
            }

            return(true);
        }
コード例 #20
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;
        }
コード例 #21
0
ファイル: ProtoObjectPlant.cs プロジェクト: jashking/CryoFall
 public override bool SharedCanInteract(ICharacter character, IStaticWorldObject worldObject, bool writeToLog)
 {
     return(PveSystem.SharedValidateInteractionIsNotForbidden(character, worldObject, writeToLog) &&
            NewbieProtectionSystem.SharedValidateInteractionIsNotForbidden(character, worldObject, writeToLog) &&
            this.SharedIsInsideCharacterInteractionArea(character, worldObject, writeToLog));
 }
コード例 #22
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);
        }
コード例 #23
0
        public static double ServerCalculateTotalDamage(
            WeaponFinalCache weaponCache,
            IWorldObject targetObject,
            FinalStatsCache targetFinalStatsCache,
            double damagePreMultiplier,
            bool clampDefenseTo1)
        {
            if (targetObject is IStaticWorldObject staticWorldObject &&
                (!RaidingProtectionSystem.SharedCanRaid(staticWorldObject,
                                                        showClientNotification: false) ||
                 !LandClaimShieldProtectionSystem.SharedCanRaid(staticWorldObject,
                                                                showClientNotification: false) ||
                 !PveSystem.SharedIsAllowStaticObjectDamage(weaponCache.Character,
                                                            staticWorldObject,
                                                            showClientNotification: false) ||
                 !NewbieProtectionSystem.SharedIsAllowStructureDamage(weaponCache.Character,
                                                                      staticWorldObject,
                                                                      showClientNotification: false)))
            {
                return(0);
            }

            if (targetObject.ProtoGameObject is IProtoVehicle &&
                !PveSystem.SharedIsAllowVehicleDamage(weaponCache,
                                                      (IDynamicWorldObject)targetObject,
                                                      showClientNotification: false))
            {
                return(0);
            }

            if (weaponCache.ProtoExplosive is not null &&
                targetObject is IStaticWorldObject targetStaticWorldObject)
            {
                // special case - apply the explosive damage to static object
                return(ServerCalculateTotalDamageByExplosive(weaponCache.Character,
                                                             weaponCache.ProtoExplosive,
                                                             targetStaticWorldObject,
                                                             damagePreMultiplier));
            }

            if (ServerIsRestrictedPvPDamage(weaponCache,
                                            targetObject,
                                            out var isPvPcase,
                                            out var isFriendlyFireCase))
            {
                return(0);
            }

            var damageValue = damagePreMultiplier * weaponCache.DamageValue;
            var invertedArmorPiercingCoef = weaponCache.InvertedArmorPiercingCoef;

            var totalDamage = 0d;

            // calculate total damage by summing all the damage components
            foreach (var damageDistribution in weaponCache.DamageDistributions)
            {
                var defenseStatName = SharedGetDefenseStatName(damageDistribution.DamageType);
                var defenseFraction = targetFinalStatsCache[defenseStatName];
                defenseFraction = MathHelper.Clamp(defenseFraction, 0, clampDefenseTo1 ? 1 : double.MaxValue);

                totalDamage += ServerCalculateDamageComponent(
                    damageValue,
                    invertedArmorPiercingCoef,
                    damageDistribution,
                    defenseFraction);
            }

            // multiply on final multiplier (usually used for expanding projectiles)
            totalDamage *= weaponCache.FinalDamageMultiplier;

            var damagingCharacter = weaponCache.Character;

            if (isPvPcase)
            {
                // apply PvP damage multiplier
                totalDamage *= WeaponConstants.DamagePvpMultiplier;
            }
            else if (damagingCharacter is not null &&
                     !damagingCharacter.IsNpc &&
                     targetObject.ProtoGameObject
                     is IProtoCharacterMob protoCharacterMob &&
                     !protoCharacterMob.IsBoss)
            {
                // apply PvE damage multiplier
                totalDamage *= WeaponConstants.DamagePveMultiplier;
            }
コード例 #24
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);
            }