public DropItemContext( ICharacter character, IStaticWorldObject staticWorldObject = null, IProtoItemWeapon byWeaponProto = null, IProtoExplosive byExplosiveProto = null) { this.ByWeaponProto = byWeaponProto; this.ByExplosiveProto = byExplosiveProto; this.character = character; this.staticWorldObject = staticWorldObject; }
public static void ServerExplode( [CanBeNull] ICharacter character, [CanBeNull] IProtoExplosive protoExplosive, [CanBeNull] IProtoItemWeapon protoWeapon, ExplosionPreset explosionPreset, Vector2D epicenterPosition, DamageDescription damageDescriptionCharacters, IPhysicsSpace physicsSpace, ExecuteExplosionDelegate executeExplosionCallback) { ValidateIsServer(); // schedule explosion charred ground spawning var protoObjectCharredGround = explosionPreset.ProtoObjectCharredGround; if (protoObjectCharredGround is not null) { ServerTimersSystem.AddAction( delaySeconds: explosionPreset.SpriteAnimationDuration * 0.5, () => { var tilePosition = (Vector2Ushort)(epicenterPosition - protoObjectCharredGround.Layout.Center); var canSpawnCharredGround = true; var tile = Server.World.GetTile(tilePosition); if (tile.ProtoTile.Kind != TileKind.Solid || tile.EightNeighborTiles.Any(t => t.ProtoTile.Kind != TileKind.Solid)) { // allow charred ground only on solid ground canSpawnCharredGround = false; } if (canSpawnCharredGround) { // remove existing charred ground objects at the same tile foreach (var staticWorldObject in Shared.WrapInTempList( tile.StaticObjects) .EnumerateAndDispose()) { switch (staticWorldObject.ProtoStaticWorldObject) { case ProtoObjectCharredGround _: Server.World.DestroyObject(staticWorldObject); break; case IProtoObjectDeposit _: // don't show charred ground over resource deposits (it looks wrong) canSpawnCharredGround = false; break; } } } if (canSpawnCharredGround && PveSystem.ServerIsPvE) { var bounds = protoObjectCharredGround.Layout.Bounds; if (LandClaimSystem.SharedIsLandClaimedByAnyone( new RectangleInt(tilePosition, bounds.Size + (1, 1)))) { // ensure that it's not possible to create charred ground in a land claim area in PvE canSpawnCharredGround = false; } } if (canSpawnCharredGround) { // spawn charred ground var objectCharredGround = Server.World.CreateStaticWorldObject(protoObjectCharredGround, tilePosition); var objectCharredGroundOffset = epicenterPosition - tilePosition.ToVector2D(); if (objectCharredGroundOffset != Vector2D.Zero) { ProtoObjectCharredGround.ServerSetWorldOffset(objectCharredGround, (Vector2F)objectCharredGroundOffset); } } }); } // schedule explosion damage ServerTimersSystem.AddAction( delaySeconds: explosionPreset.ServerDamageApplyDelay, () => { // prepare weapon caches var characterFinalStatsCache = character?.SharedGetFinalStatsCache() ?? FinalStatsCache.Empty; var weaponFinalCache = new WeaponFinalCache(character, characterFinalStatsCache, weapon: null, protoWeapon: protoWeapon, protoAmmo: null, damageDescription: damageDescriptionCharacters, protoExplosive: protoExplosive); // execute explosion executeExplosionCallback( positionEpicenter: epicenterPosition, physicsSpace: physicsSpace, weaponFinalCache: weaponFinalCache); }); }
public WeaponFinalCache( ICharacter character, FinalStatsCache characterFinalStatsCache, [CanBeNull] IItem weapon, [CanBeNull] IProtoItemWeapon protoWeapon, [CanBeNull] IProtoItemAmmo protoAmmo, DamageDescription damageDescription, IProtoExplosive protoExplosive = null, IDynamicWorldObject objectDrone = null) { this.Character = character; this.CharacterFinalStatsCache = characterFinalStatsCache; this.Drone = objectDrone; this.Weapon = weapon; this.ProtoWeapon = (IProtoItemWeapon)weapon?.ProtoItem ?? protoWeapon; this.ProtoAmmo = protoAmmo; this.ProtoExplosive = protoExplosive; if (damageDescription is null) { // TODO: it looks like not implemented yet and we should throw an exception here // fallback in case weapon don't provide damage description (such as no-ammo weapon) damageDescription = new DamageDescription( damageValue: 0, armorPiercingCoef: 0, finalDamageMultiplier: 1, rangeMax: 0, damageDistribution: new DamageDistribution()); } var descriptionDamages = damageDescription.DamageProportions; var damageDistributionsCount = descriptionDamages.Count; var resultDamageDistributions = new List <DamageProportion>(damageDistributionsCount); var totalPercents = 0d; for (var index = 0; index < damageDistributionsCount; index++) { var source = descriptionDamages[index]; var statName = GetProportionStatName(source.DamageType); var resultDamageProportion = source.Proportion + characterFinalStatsCache[statName]; if (resultDamageProportion <= 0) { continue; } resultDamageDistributions.Add(new DamageProportion(source.DamageType, resultDamageProportion)); totalPercents += resultDamageProportion; } if (damageDistributionsCount > 0 && Math.Abs(totalPercents - 1) > 0.001d) { throw new Exception( "Sum of all damage proportions must be exactly 1. Calculated value: " + totalPercents.ToString("F3")); } this.DamageDistributions = resultDamageDistributions; this.DamageValue = damageDescription.DamageValue * (protoWeapon?.DamageMultiplier ?? 1.0) + characterFinalStatsCache[StatName.DamageAdd]; var weaponSkillProto = protoWeapon?.WeaponSkillProto; if (weaponSkillProto is not null) { var statName = protoWeapon.WeaponSkillProto.StatNameDamageBonusMultiplier; this.DamageValue *= characterFinalStatsCache.GetMultiplier(statName); } this.RangeMax = damageDescription.RangeMax * (protoWeapon?.RangeMultiplier ?? 1.0) + characterFinalStatsCache[StatName.AttackRangeMax]; var armorPiercingCoef = (1 + characterFinalStatsCache[StatName.AttackArmorPiercingMultiplier]) * (damageDescription.ArmorPiercingCoef + characterFinalStatsCache[StatName.AttackArmorPiercingValue]); this.InvertedArmorPiercingCoef = 1 - armorPiercingCoef; this.FinalDamageMultiplier = damageDescription.FinalDamageMultiplier + characterFinalStatsCache[StatName.AttackFinalDamageMultiplier]; var probability = protoWeapon?.SpecialEffectProbability ?? 0; if (weaponSkillProto is not null) { var statNameSpecialEffectChance = weaponSkillProto.StatNameSpecialEffectChanceMultiplier; probability *= characterFinalStatsCache.GetMultiplier(statNameSpecialEffectChance); } this.SpecialEffectProbability = probability; this.FireScatterPreset = protoAmmo?.OverrideFireScatterPreset ?? protoWeapon?.FireScatterPreset ?? default; var shotsPerFire = this.FireScatterPreset.ProjectileAngleOffets.Length; if (shotsPerFire > 1) { // decrease final damage and special effect multiplier on the number of shots per fire var coef = 1.0 / shotsPerFire; this.FinalDamageMultiplier *= coef; this.SpecialEffectProbability *= coef; } }