protected override double SharedCalculateDamageByWeapon( WeaponFinalCache weaponCache, double damagePreMultiplier, IStaticWorldObject targetObject, out double obstacleBlockDamageCoef) { obstacleBlockDamageCoef = 1; if (!PveSystem.SharedIsAllowStructureDamage(weaponCache.Character, targetObject, showClientNotification: false)) { return(0); } if (weaponCache.ProtoWeapon is IProtoItemToolWoodcutting protoItemToolWoodCutting) { // get damage multiplier ("woodcutting speed") var damageMultiplier = weaponCache.Character .SharedGetFinalStatMultiplier(StatName.WoodcuttingSpeed); return(protoItemToolWoodCutting.DamageToTree * damageMultiplier * ToolsConstants.ActionWoodcuttingSpeedMultiplier); } if (weaponCache.ProtoWeapon is ItemNoWeapon) { // no damage with hands if (IsClient) { NotificationSystem.ClientShowNotification(NotificationUseAxe, icon: this.Icon); } return(0); } // not a wood-cutting tool - call default damage apply method return(base.SharedCalculateDamageByWeapon(weaponCache, damagePreMultiplier, targetObject, out obstacleBlockDamageCoef)); }
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); PveSystem.SharedIsAllowStructureDamage(weaponCache.Character, targetObject, showClientNotification: true); 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); }
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); }