Example #1
0
 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;
 }
Example #2
0
        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);
            });
        }
Example #3
0
        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;
            }
        }