Ejemplo n.º 1
0
        public FactionLogEntryFactionBaseRaided(Vector2Ushort basePosition, [CanBeNull] ICharacter raiderCharacter)
            : base(byMember: null)
        {
            if (raiderCharacter is not null)
            {
                this.RaiderPlayerName = raiderCharacter.Name;
                this.RaiderClanTag    = FactionSystem.SharedGetClanTag(raiderCharacter);
            }

            this.BasePosition = basePosition;
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Check whether it was a player character killed and check its damage sources history.
        /// If it was killed by a friendly faction member, punish that member by reducing its karma
        /// and kick if it's too low.
        /// Please note: party members could kill each other without consequences even if they're in the same faction.
        /// </summary>
        private static void ServerCharacterDeathHandler(ICharacter killedCharacter)
        {
            if (killedCharacter.IsNpc)
            {
                return;
            }

            // get damage sources for the past 12.5 seconds (2.5 chunks)
            var damageSources = CharacterDamageTrackingSystem.ServerGetDamageSourcesAfterTime(
                killedCharacter,
                afterTime: Server.Game.FrameTime
                - ServerCharacterDamageSourcesStats.ChunkDuration * 2.5);

            if (damageSources is null)
            {
                return;
            }

            var killedCharacterFaction = FactionSystem.ServerGetFaction(killedCharacter);

            if (killedCharacterFaction is null)
            {
                // a non-faction player character was killed - no karma system action
                return;
            }

            foreach (var entry in damageSources)
            {
                if (entry.ProtoEntity is not PlayerCharacter)
                {
                    continue;
                }

                var attackerCharacter = Server.Characters.GetPlayerCharacter(entry.Name);
                if (attackerCharacter is null)
                {
                    // should not be possible
                    continue;
                }

                ProcessAttackerCharacter(attackerCharacter, damageFraction: entry.Fraction);
            }

            void ProcessAttackerCharacter(ICharacter attackerCharacter, double damageFraction)
            {
                // found a player character that has damaged the (now dead) player character recently
                if (PartySystem.ServerIsSameParty(killedCharacter, attackerCharacter))
                {
                    // damaging your own party members is fine
                    return;
                }

                var attackerCharacterFaction = FactionSystem.ServerGetFaction(attackerCharacter);

                if (attackerCharacterFaction is null ||
                    Faction.GetPublicState(attackerCharacterFaction).Kind != FactionKind.Public)
                {
                    // attacking character is not a member of a public faction - no karma system action
                    return;
                }

                if (!ReferenceEquals(killedCharacterFaction, attackerCharacterFaction) &&
                    (FactionSystem.ServerGetFactionDiplomacyStatus(killedCharacterFaction,
                                                                   attackerCharacterFaction)
                     != FactionDiplomacyStatus.Ally))
                {
                    // the killer is not a member of the same faction, and not an ally faction's member
                    return;
                }

                // a friendly player character has been killed, apply the punishment
                var attackerCharacterPrivateState = PlayerCharacter.GetPrivateState(attackerCharacter);
                var karmaPenalty = KarmaPenaltyPerKill * Math.Min(1, damageFraction * 2.0);

                attackerCharacterPrivateState.ServerFactionKarma -= karmaPenalty;
                Logger.Info(
                    string.Format(
                        "{0} karma decreased by {1:0.##} to {2:0.##} - due to {3:0.##}% recent damage to killed character {4}",
                        attackerCharacter,
                        karmaPenalty,
                        attackerCharacterPrivateState.ServerFactionKarma,
                        Math.Round(damageFraction * 100),
                        killedCharacter));

                // notify about the decreased karma
                Instance.CallClient(attackerCharacter,
                                    _ => _.ClientRemote_OnKarmaDecreased());

                if (attackerCharacterPrivateState.ServerFactionKarma > KarmaFactionKickThreshold)
                {
                    // didn't reach the kick threshold
                    return;
                }

                // that's enough! Bye-bye, killer!
                if (FactionSystem.ServerGetRole(attackerCharacter) == FactionMemberRole.Leader)
                {
                    if (FactionSystem.ServerGetFactionMembersReadOnly(attackerCharacterFaction).Count == 1)
                    {
                        // we cannot kick a faction leader as it's the only member in a faction
                        // but it also means that it didn't kill a faction member but an ally faction's member
                        // just drop the alliance
                        Logger.Important(
                            string.Format(
                                "Leader of the public faction [{0}] with just a single member ({1}) killed a member ({2}) of an ally faction [{3}] and reached a too low karma level - break the alliance",
                                FactionSystem.SharedGetClanTag(attackerCharacterFaction),
                                attackerCharacter,
                                killedCharacter,
                                FactionSystem.SharedGetClanTag(killedCharacterFaction)));

                        FactionSystem.ServerSetFactionDiplomacyStatusNeutral(killedCharacterFaction,
                                                                             attackerCharacterFaction);
                        // reset the karma a bit - restore an equivalent of a half of the karma penalty for killing
                        attackerCharacterPrivateState.ServerFactionKarma += KarmaPenaltyPerKill / 2.0;
                        return;
                    }

                    // pass the faction ownership to a different faction member and kick the killer character
                    var newLeaderName = FactionSystem.ServerGetFactionMembersReadOnly(attackerCharacterFaction)
                                        .FirstOrDefault(m => m.Role != FactionMemberRole.Member &&
                                                        m.Role != FactionMemberRole.Leader)
                                        .Name;

                    if (string.IsNullOrEmpty(newLeaderName))
                    {
                        newLeaderName = FactionSystem.ServerGetFactionMembersReadOnly(attackerCharacterFaction)
                                        .First(m => m.Role != FactionMemberRole.Leader)
                                        .Name;
                    }

                    Logger.Important(
                        string.Format(
                            "Leader of the public faction [{0}] - {1} - killed a friendly player ({2}) and reached a too low karma level - ownership of the faction will be passed to {3}. Player {1} will be kicked from the faction.",
                            FactionSystem.SharedGetClanTag(attackerCharacterFaction),
                            attackerCharacter,
                            killedCharacter,
                            newLeaderName));

                    // It may case a warning message on the client side
                    // as the faction leader is later removed from the faction
                    // but it expects to receive the updated faction information.
                    FactionSystem.ServerTransferFactionOwnership(attackerCharacter, newLeaderName);
                }

                Logger.Important(
                    string.Format(
                        "Member of a public faction [{0}] - {1} - killed a friendly player ({2} from [{3}]) and reached a too low karma level. Player {1} will be kicked from the faction.",
                        FactionSystem.SharedGetClanTag(attackerCharacterFaction),
                        attackerCharacter,
                        killedCharacter,
                        FactionSystem.SharedGetClanTag(killedCharacterFaction)));

                FactionSystem.ServerRemoveMemberFromCurrentFaction(attackerCharacter);

                Instance.CallClient(attackerCharacter,
                                    _ => _.ClientRemote_OnKickedFromTheFactionDueToLowKarma());
            }
        }