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); }
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); }
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.")); }
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); }
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); }
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)); }
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; }
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; }
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); }
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); }
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); }