Ejemplo n.º 1
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);
        }
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());
            }
        }