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); }
/// <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()); } }