private void ServerExplode(IStaticWorldObject worldObject, ICharacter character) { try { Logger.Important(worldObject + " exploded"); this.ServerSendObjectDestroyedEvent(worldObject); ExplosionHelper.ServerExplode( character: character, protoExplosive: this, protoWeapon: null, explosionPreset: this.ExplosionPreset, epicenterPosition: worldObject.TilePosition.ToVector2D() + this.Layout.Center, damageDescriptionCharacters: this.damageDescriptionCharacters, physicsSpace: worldObject.PhysicsBody.PhysicsSpace, executeExplosionCallback: this.ServerExecuteExplosion); if (this.IsActivatesRaidModeForLandClaim) { var explosionRadius = (int)Math.Ceiling(this.DamageRadius); var bounds = new RectangleInt(x: worldObject.TilePosition.X - explosionRadius, y: worldObject.TilePosition.Y - explosionRadius, width: 2 * explosionRadius, height: 2 * explosionRadius); if (RaidingProtectionSystem.SharedCanRaid(bounds, showClientNotification: false)) { // try activate the raidblock LandClaimSystem.ServerOnRaid(bounds, character); } else { // Raiding is not possible now due to raiding window // Find if there is any land claim and in that case notify nearby players // that the damage to objects there were not applied. if (LandClaimSystem.SharedIsLandClaimedByAnyone(bounds)) { using var tempPlayers = Api.Shared.GetTempList <ICharacter>(); Server.World.GetScopedByPlayers(worldObject, tempPlayers); RaidingProtectionSystem.ServerNotifyShowNotificationRaidingNotAvailableNow( tempPlayers.AsList()); } } } } finally { Server.World.DestroyObject(worldObject); } }
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; }
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); }
private void ServerExplode(IItem item) { Server.Items.DestroyItem(item); // explode a primitive bomb in player position var character = item.Container.OwnerAsCharacter; if (character is null) { return; } var characterPublicState = character.GetPublicState <PlayerCharacterPublicState>(); if (!characterPublicState.IsDead) { // ensure the wearer is killed by the explosion characterPublicState.CurrentStats.ServerSetHealthCurrent(0); } var targetPosition = character.Position; var protoBomb = ProtoObjectBomb; SharedExplosionHelper.ServerExplode( character: character, protoExplosive: protoBomb, protoWeapon: null, explosionPreset: protoBomb.ExplosionPreset, epicenterPosition: targetPosition, damageDescriptionCharacters: protoBomb.DamageDescriptionCharacters, physicsSpace: Server.World.GetPhysicsSpace(), executeExplosionCallback: protoBomb.ServerExecuteExplosion); // notify all characters about the explosion using var charactersObserving = Api.Shared.GetTempList <ICharacter>(); const byte explosionEventRadius = 40; Server.World.GetCharactersInRadius(targetPosition.ToVector2Ushort(), charactersObserving, radius: explosionEventRadius, onlyPlayers: true); this.CallClient(charactersObserving.AsList(), _ => _.ClientRemote_OnExplosion(targetPosition)); // activate the raidblock if possible (the code is similar to ProtoObjectEplosive) var explosionRadius = (int)Math.Ceiling(protoBomb.DamageRadius); var bounds = new RectangleInt(x: (int)Math.Round(character.Position.X - explosionRadius), y: (int)Math.Round(character.Position.Y - explosionRadius), width: 2 * explosionRadius, height: 2 * explosionRadius); if (RaidingProtectionSystem.SharedCanRaid(bounds, showClientNotification: false)) { // try activate the raidblock LandClaimSystem.ServerOnRaid(bounds, character, isStructureDestroyed: false); } else { // Raiding is not possible now due to raiding window // Find if there is any land claim and in that case notify nearby players // that the damage to objects there was not applied. if (LandClaimSystem.SharedIsLandClaimedByAnyone(bounds)) { using var tempPlayers = Api.Shared.GetTempList <ICharacter>(); Server.World.GetScopedByPlayers(character, tempPlayers); RaidingProtectionSystem.ServerNotifyShowNotificationRaidingNotAvailableNow( tempPlayers.AsList()); } } }
public static double ServerCalculateTotalDamage( WeaponFinalCache weaponCache, IWorldObject targetObject, FinalStatsCache targetFinalStatsCache, double damagePreMultiplier, bool clampDefenseTo1) { if (targetObject is IStaticWorldObject staticWorldObject && (!RaidingProtectionSystem.SharedCanRaid(staticWorldObject, showClientNotification: false) || !PveSystem.SharedIsAllowStructureDamage(weaponCache.Character, staticWorldObject, showClientNotification: false))) { return(0); } if (weaponCache.ProtoObjectExplosive != null && targetObject.ProtoWorldObject is IProtoStaticWorldObject targetStaticWorldObjectProto) { // special case - apply the explosive damage return(ServerCalculateTotalDamageByExplosive(weaponCache.ProtoObjectExplosive, targetStaticWorldObjectProto, damagePreMultiplier)); } // these two cases apply only if damage dealt not by a bomb 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 != null && !damagingCharacter.IsNpc) { // apply PvE damage multiplier totalDamage *= WeaponConstants.DamagePveMultiplier; } else if (damagingCharacter != null && damagingCharacter.IsNpc) { // apply creature damage multiplier totalDamage *= WeaponConstants.DamageCreaturesMultiplier; if (targetObject is ICharacter victim && !victim.ServerIsOnline && !victim.IsNpc) { // don't deal creature damage to offline players totalDamage = 0; } } if (isFriendlyFireCase) { totalDamage *= WeaponConstants.DamageFriendlyFireMultiplier; } return(totalDamage); }