Exemple #1
0
 static float GetLethalDamage(ItemComponent weapon)
 {
     float lethalDmg = 0;
     Attack attack = GetAttackDefinition(weapon);
     if (attack != null)
     {
         lethalDmg = attack.GetTotalDamage();
     }
     return lethalDmg;
 }
Exemple #2
0
 private Item GetWeapon(IEnumerable<ItemComponent> weaponList, out ItemComponent weaponComponent)
 {
     weaponComponent = null;
     float bestPriority = 0;
     float lethalDmg = -1;
     bool enemyIsClose = EnemyIsClose();
     foreach (var weapon in weaponList)
     {
         float priority = weapon.CombatPriority;
         if (!IsLoaded(weapon))
         {
             if (weapon is RangedWeapon && enemyIsClose)
             {
                 // Close to the enemy. Ignore weapons that don't have any ammunition (-> Don't seek ammo).
                 continue;
             }
             else
             {
                 // Halve the priority for weapons that don't have proper ammunition loaded.
                 priority /= 2;
             }
         }
         if (Enemy.Stun > 1)
         {
             // Enemy is stunned, reduce the priority of stunner weapons.
             Attack attack = GetAttackDefinition(weapon);
             if (attack != null)
             {
                 lethalDmg = attack.GetTotalDamage();
                 float max = lethalDmg + 1;
                 if (weapon.Item.HasTag("stunner"))
                 {
                     priority = max;
                 }
                 else
                 {
                     float stunDmg = ApproximateStunDamage(weapon, attack);
                     float diff = stunDmg - lethalDmg;
                     priority = Math.Clamp(priority - Math.Max(diff * 2, 0), min: 1, max);
                 }
             }
         }
         else if (Mode == CombatMode.Arrest)
         {
             // Enemy is not stunned, increase the priority of stunner weapons and decrease the priority of lethal weapons.
             if (weapon.Item.HasTag("stunner"))
             {
                 priority *= 2;
             }
             else
             {
                 Attack attack = GetAttackDefinition(weapon);
                 if (attack != null)
                 {
                     lethalDmg = attack.GetTotalDamage();
                     float stunDmg = ApproximateStunDamage(weapon, attack);
                     float diff = stunDmg - lethalDmg;
                     if (diff < 0)
                     {
                         priority /= 2;
                     }
                 }
             }
         }
         else if (weapon is MeleeWeapon && weapon.Item.HasTag("stunner") && !CanMeleeStunnerStun(weapon))
         {
             Attack attack = GetAttackDefinition(weapon);
             priority = attack?.GetTotalDamage() ?? priority / 2;
         }
         if (priority > bestPriority)
         {
             weaponComponent = weapon;
             bestPriority = priority;
         }
     }
     if (weaponComponent == null) { return null; }
     if (bestPriority < 1) { return null; }
     if (Mode == CombatMode.Arrest)
     {
         if (weaponComponent.Item.HasTag("stunner"))
         {
             isLethalWeapon = false;
         }
         else
         {
             if (lethalDmg < 0)
             {
                 lethalDmg = GetLethalDamage(weaponComponent);
             }
             isLethalWeapon = lethalDmg > 1;
         }
         if (allowHoldFire && !hasAimed && holdFireTimer <= 0)
         {
             holdFireTimer = arrestHoldFireTime * Rand.Range(0.75f, 1.25f);
         }
     }
     return weaponComponent.Item;
Exemple #3
0
        public void Explode(Vector2 worldPosition, Entity damageSource, Character attacker = null)
        {
            prevExplosions.Add(new Triplet <Explosion, Vector2, float>(this, worldPosition, (float)Timing.TotalTime));
            if (prevExplosions.Count > 100)
            {
                prevExplosions.RemoveAt(0);
            }

            Hull hull = Hull.FindHull(worldPosition);

            ExplodeProjSpecific(worldPosition, hull);

            if (hull != null && !string.IsNullOrWhiteSpace(decal) && decalSize > 0.0f)
            {
                hull.AddDecal(decal, worldPosition, decalSize, true);
            }

            float displayRange = attack.Range;

            Vector2 cameraPos  = Character.Controlled != null ? Character.Controlled.WorldPosition : GameMain.GameScreen.Cam.Position;
            float   cameraDist = Vector2.Distance(cameraPos, worldPosition) / 2.0f;

            GameMain.GameScreen.Cam.Shake = cameraShake * Math.Max((cameraShakeRange - cameraDist) / cameraShakeRange, 0.0f);
#if CLIENT
            if (screenColor != Color.Transparent)
            {
                Color flashColor = Color.Lerp(Color.Transparent, screenColor, Math.Max((screenColorRange - cameraDist) / screenColorRange, 0.0f));
                Screen.Selected.ColorFade(flashColor, Color.Transparent, screenColorDuration);
            }
#endif

            if (displayRange < 0.1f)
            {
                return;
            }

            if (attack.GetStructureDamage(1.0f) > 0.0f)
            {
                RangedStructureDamage(worldPosition, displayRange, attack.GetStructureDamage(1.0f), attacker);
            }

            if (EmpStrength > 0.0f)
            {
                float displayRangeSqr = displayRange * displayRange;
                foreach (Item item in Item.ItemList)
                {
                    float distSqr = Vector2.DistanceSquared(item.WorldPosition, worldPosition);
                    if (distSqr > displayRangeSqr)
                    {
                        continue;
                    }

                    float distFactor = 1.0f - (float)Math.Sqrt(distSqr) / displayRange;

                    //damage repairable power-consuming items
                    var powered = item.GetComponent <Powered>();
                    if (powered == null || !powered.VulnerableToEMP)
                    {
                        continue;
                    }
                    if (item.Repairables.Any())
                    {
                        item.Condition -= item.MaxCondition * EmpStrength * distFactor;
                    }

                    //discharge batteries
                    var powerContainer = item.GetComponent <PowerContainer>();
                    if (powerContainer != null)
                    {
                        powerContainer.Charge -= powerContainer.Capacity * EmpStrength * distFactor;
                    }
                }
            }

            if (MathUtils.NearlyEqual(force, 0.0f) && MathUtils.NearlyEqual(attack.Stun, 0.0f) && MathUtils.NearlyEqual(attack.GetTotalDamage(false), 0.0f))
            {
                return;
            }

            DamageCharacters(worldPosition, attack, force, damageSource, attacker);

            if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
            {
                foreach (Item item in Item.ItemList)
                {
                    if (item.Condition <= 0.0f)
                    {
                        continue;
                    }
                    if (Vector2.Distance(item.WorldPosition, worldPosition) > attack.Range * 0.5f)
                    {
                        continue;
                    }
                    if (applyFireEffects && !item.FireProof)
                    {
                        //don't apply OnFire effects if the item is inside a fireproof container
                        //(or if it's inside a container that's inside a fireproof container, etc)
                        Item container = item.Container;
                        bool fireProof = false;
                        while (container != null)
                        {
                            if (container.FireProof)
                            {
                                fireProof = true;
                                break;
                            }
                            container = container.Container;
                        }
                        if (!fireProof)
                        {
                            item.ApplyStatusEffects(ActionType.OnFire, 1.0f);
                            if (item.Condition <= 0.0f && GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
                            {
                                GameMain.NetworkMember.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnFire });
                            }
                        }
                    }

                    if (item.Prefab.DamagedByExplosions && !item.Indestructible)
                    {
                        float limbRadius = item.body == null ? 0.0f : item.body.GetMaxExtent();
                        float dist       = Vector2.Distance(item.WorldPosition, worldPosition);
                        dist = Math.Max(0.0f, dist - ConvertUnits.ToDisplayUnits(limbRadius));
                        if (dist > attack.Range)
                        {
                            continue;
                        }
                        float distFactor   = 1.0f - dist / attack.Range;
                        float damageAmount = attack.GetItemDamage(1.0f) * item.Prefab.ExplosionDamageMultiplier;
                        item.Condition -= damageAmount * distFactor;
                    }
                }
            }
        }
Exemple #4
0
        public void Explode(Vector2 worldPosition, Entity damageSource, Character attacker = null)
        {
            prevExplosions.Add(new Triplet <Explosion, Vector2, float>(this, worldPosition, (float)Timing.TotalTime));
            if (prevExplosions.Count > 100)
            {
                prevExplosions.RemoveAt(0);
            }

            Hull hull = Hull.FindHull(worldPosition);

            ExplodeProjSpecific(worldPosition, hull);

            float displayRange = attack.Range;

            if (displayRange < 0.1f)
            {
                return;
            }

            Vector2 cameraPos  = Character.Controlled != null ? Character.Controlled.WorldPosition : GameMain.GameScreen.Cam.Position;
            float   cameraDist = Vector2.Distance(cameraPos, worldPosition) / 2.0f;

            GameMain.GameScreen.Cam.Shake = CameraShake * Math.Max((displayRange - cameraDist) / displayRange, 0.0f);

            if (attack.GetStructureDamage(1.0f) > 0.0f)
            {
                RangedStructureDamage(worldPosition, displayRange, attack.GetStructureDamage(1.0f), attacker);
            }

            if (empStrength > 0.0f)
            {
                float displayRangeSqr = displayRange * displayRange;
                foreach (Item item in Item.ItemList)
                {
                    float distSqr = Vector2.DistanceSquared(item.WorldPosition, worldPosition);
                    if (distSqr > displayRangeSqr)
                    {
                        continue;
                    }

                    float distFactor = 1.0f - (float)Math.Sqrt(distSqr) / displayRange;

                    //damage repairable power-consuming items
                    var powered = item.GetComponent <Powered>();
                    if (powered == null || !powered.VulnerableToEMP)
                    {
                        continue;
                    }
                    if (item.Repairables.Any())
                    {
                        item.Condition -= 100 * empStrength * distFactor;
                    }

                    //discharge batteries
                    var powerContainer = item.GetComponent <PowerContainer>();
                    if (powerContainer != null)
                    {
                        powerContainer.Charge -= powerContainer.Capacity * empStrength * distFactor;
                    }
                }
            }

            if (force == 0.0f && attack.Stun == 0.0f && attack.GetTotalDamage(false) == 0.0f)
            {
                return;
            }

            DamageCharacters(worldPosition, attack, force, damageSource, attacker);

            if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
            {
                if (flames)
                {
                    foreach (Item item in Item.ItemList)
                    {
                        if (item.CurrentHull != hull || item.FireProof || item.Condition <= 0.0f)
                        {
                            continue;
                        }

                        //don't apply OnFire effects if the item is inside a fireproof container
                        //(or if it's inside a container that's inside a fireproof container, etc)
                        Item container = item.Container;
                        while (container != null)
                        {
                            if (container.FireProof)
                            {
                                return;
                            }
                            container = container.Container;
                        }

                        if (Vector2.Distance(item.WorldPosition, worldPosition) > attack.Range * 0.1f)
                        {
                            continue;
                        }

                        item.ApplyStatusEffects(ActionType.OnFire, 1.0f);

                        if (item.Condition <= 0.0f && GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
                        {
                            GameMain.NetworkMember.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnFire });
                        }
                    }
                }
            }
        }
Exemple #5
0
        public void Explode(Vector2 worldPosition, Entity damageSource, Character attacker = null)
        {
            Hull hull = Hull.FindHull(worldPosition);

            ExplodeProjSpecific(worldPosition, hull);

            if (hull != null && !string.IsNullOrWhiteSpace(decal) && decalSize > 0.0f)
            {
                hull.AddDecal(decal, worldPosition, decalSize, isNetworkEvent: false);
            }

            float displayRange = Attack.Range;

            if (damageSource is Item sourceItem)
            {
                displayRange            *= 1.0f + sourceItem.GetQualityModifier(Quality.StatType.ExplosionRadius);
                Attack.DamageMultiplier *= 1.0f + sourceItem.GetQualityModifier(Quality.StatType.ExplosionDamage);
            }

            Vector2 cameraPos  = GameMain.GameScreen.Cam.Position;
            float   cameraDist = Vector2.Distance(cameraPos, worldPosition) / 2.0f;

            GameMain.GameScreen.Cam.Shake = cameraShake * Math.Max((cameraShakeRange - cameraDist) / cameraShakeRange, 0.0f);
#if CLIENT
            if (screenColor != Color.Transparent)
            {
                Color flashColor = Color.Lerp(Color.Transparent, screenColor, Math.Max((screenColorRange - cameraDist) / screenColorRange, 0.0f));
                Screen.Selected.ColorFade(flashColor, Color.Transparent, screenColorDuration);
            }
#endif

            if (displayRange < 0.1f)
            {
                return;
            }

            if (!MathUtils.NearlyEqual(Attack.GetStructureDamage(1.0f), 0.0f) || !MathUtils.NearlyEqual(Attack.GetLevelWallDamage(1.0f), 0.0f))
            {
                RangedStructureDamage(worldPosition, displayRange, Attack.GetStructureDamage(1.0f), Attack.GetLevelWallDamage(1.0f), attacker);
            }

            if (BallastFloraDamage > 0.0f)
            {
                RangedBallastFloraDamage(worldPosition, displayRange, BallastFloraDamage, attacker);
            }

            if (EmpStrength > 0.0f)
            {
                float displayRangeSqr = displayRange * displayRange;
                foreach (Item item in Item.ItemList)
                {
                    float distSqr = Vector2.DistanceSquared(item.WorldPosition, worldPosition);
                    if (distSqr > displayRangeSqr)
                    {
                        continue;
                    }

                    float distFactor = 1.0f - (float)Math.Sqrt(distSqr) / displayRange;

                    //damage repairable power-consuming items
                    var powered = item.GetComponent <Powered>();
                    if (powered == null || !powered.VulnerableToEMP)
                    {
                        continue;
                    }
                    if (item.Repairables.Any())
                    {
                        item.Condition -= item.MaxCondition * EmpStrength * distFactor;
                    }

                    //discharge batteries
                    var powerContainer = item.GetComponent <PowerContainer>();
                    if (powerContainer != null)
                    {
                        powerContainer.Charge -= powerContainer.Capacity * EmpStrength * distFactor;
                    }
                }
            }

            if (itemRepairStrength > 0.0f)
            {
                float displayRangeSqr = displayRange * displayRange;
                foreach (Item item in Item.ItemList)
                {
                    float distSqr = Vector2.DistanceSquared(item.WorldPosition, worldPosition);
                    if (distSqr > displayRangeSqr)
                    {
                        continue;
                    }

                    float distFactor = 1.0f - (float)Math.Sqrt(distSqr) / displayRange;
                    //repair repairable items
                    if (item.Repairables.Any())
                    {
                        item.Condition += itemRepairStrength * distFactor;
                    }
                }
            }

            if (MathUtils.NearlyEqual(force, 0.0f) && MathUtils.NearlyEqual(Attack.Stun, 0.0f) && MathUtils.NearlyEqual(Attack.GetTotalDamage(false), 0.0f) && !abilityExplosion)
            {
                return;
            }

            DamageCharacters(worldPosition, Attack, force, damageSource, attacker, applyToSelf);

            if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
            {
                foreach (Item item in Item.ItemList)
                {
                    if (item.Condition <= 0.0f)
                    {
                        continue;
                    }
                    float dist       = Vector2.Distance(item.WorldPosition, worldPosition);
                    float itemRadius = item.body == null ? 0.0f : item.body.GetMaxExtent();
                    dist = Math.Max(0.0f, dist - ConvertUnits.ToDisplayUnits(itemRadius));
                    if (dist > displayRange)
                    {
                        continue;
                    }

                    if (dist < displayRange * 0.5f && applyFireEffects && !item.FireProof && ignoreFireEffectsForTags.None(t => item.HasTag(t)))
                    {
                        //don't apply OnFire effects if the item is inside a fireproof container
                        //(or if it's inside a container that's inside a fireproof container, etc)
                        Item container = item.Container;
                        bool fireProof = false;
                        while (container != null)
                        {
                            if (container.FireProof)
                            {
                                fireProof = true;
                                break;
                            }
                            container = container.Container;
                        }
                        if (!fireProof)
                        {
                            item.ApplyStatusEffects(ActionType.OnFire, 1.0f);
                            if (item.Condition <= 0.0f && GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
                            {
                                GameMain.NetworkMember.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnFire });
                            }
                        }
                    }

                    if (item.Prefab.DamagedByExplosions && !item.Indestructible)
                    {
                        float distFactor   = 1.0f - dist / displayRange;
                        float damageAmount = Attack.GetItemDamage(1.0f) * item.Prefab.ExplosionDamageMultiplier;

                        Vector2 explosionPos = worldPosition;
                        if (item.Submarine != null)
                        {
                            explosionPos -= item.Submarine.Position;
                        }

                        damageAmount   *= GetObstacleDamageMultiplier(ConvertUnits.ToSimUnits(explosionPos), worldPosition, item.SimPosition);
                        item.Condition -= damageAmount * distFactor;
                    }
                }
            }
        }
Exemple #6
0
        private Item GetWeapon(IEnumerable <ItemComponent> weaponList, out ItemComponent weaponComponent)
        {
            weaponComponent = null;
            float bestPriority = 0;
            float lethalDmg    = -1;

            foreach (var weapon in weaponList)
            {
                // By default, the bots won't go offensive with bad weapons, unless they are close to the enemy or ordered to fight enemies.
                // NPC characters ignore this check.
                if ((initialMode == CombatMode.Offensive || initialMode == CombatMode.Arrest) && character.TeamID != Character.TeamType.FriendlyNPC)
                {
                    if (!objectiveManager.IsCurrentOrder <AIObjectiveFightIntruders>() && !EnemyIsClose())
                    {
                        if (weapon.CombatPriority < goodWeaponPriority)
                        {
                            continue;
                        }
                    }
                }

                float priority = weapon.CombatPriority;
                if (!IsLoaded(weapon))
                {
                    if (weapon is RangedWeapon && EnemyIsClose())
                    {
                        // Close to the enemy. Ignore weapons that don't have any ammunition (-> Don't seek ammo).
                        continue;
                    }
                    else
                    {
                        // Halve the priority for weapons that don't have proper ammunition loaded.
                        priority /= 2;
                    }
                }
                if (Enemy.Stun > 1)
                {
                    // Enemy is stunned, reduce the priority of stunner weapons.
                    Attack attack = GetAttackDefinition(weapon);
                    if (attack != null)
                    {
                        lethalDmg = attack.GetTotalDamage();
                        float max = lethalDmg + 1;
                        if (weapon.Item.HasTag("stunner"))
                        {
                            priority = max;
                        }
                        else
                        {
                            float stunDmg = ApproximateStunDamage(weapon, attack);
                            float diff    = stunDmg - lethalDmg;
                            priority = Math.Clamp(priority - Math.Max(diff * 2, 0), min: 1, max);
                        }
                    }
                }
                else if (Mode == CombatMode.Arrest)
                {
                    // Enemy is not stunned, increase the priority of stunner weapons and decrease the priority of lethal weapons.
                    if (weapon.Item.HasTag("stunner"))
                    {
                        priority *= 2;
                    }
                    else
                    {
                        Attack attack = GetAttackDefinition(weapon);
                        if (attack != null)
                        {
                            lethalDmg = attack.GetTotalDamage();
                            float stunDmg = ApproximateStunDamage(weapon, attack);
                            float diff    = stunDmg - lethalDmg;
                            if (diff < 0)
                            {
                                priority /= 2;
                            }
                        }
                    }
                }
                if (priority > bestPriority)
                {
                    weaponComponent = weapon;
                    bestPriority    = priority;
                }
            }
            if (weaponComponent == null)
            {
                return(null);
            }
            if (bestPriority < 1)
            {
                return(null);
            }
            if (Mode == CombatMode.Arrest)
            {
                if (weaponComponent.Item.HasTag("stunner"))
                {
                    isLethalWeapon = false;
                }
                else
                {
                    if (lethalDmg < 0)
                    {
                        lethalDmg = GetLethalDamage(weaponComponent);
                    }
                    isLethalWeapon = lethalDmg > 1;
                }
                if (allowHoldFire && !hasAimed && holdFireTimer <= 0)
                {
                    holdFireTimer = arrestHoldFireTime * Rand.Range(0.75f, 1.25f);
                }
            }
            return(weaponComponent.Item);

            bool EnemyIsClose() => character.CurrentHull == Enemy.CurrentHull || Vector2.DistanceSquared(character.Position, Enemy.Position) < 500;

            Attack GetAttackDefinition(ItemComponent weapon)
            {
                Attack attack = null;

                if (weapon is MeleeWeapon meleeWeapon)
                {
                    attack = meleeWeapon.Attack;
                }
                else if (weapon is RangedWeapon rangedWeapon)
                {
                    attack = rangedWeapon.FindProjectile(triggerOnUseOnContainers: false)?.Attack;
                }
                return(attack);
            }

            float GetLethalDamage(ItemComponent weapon)
            {
                float  lethalDmg = 0;
                Attack attack    = GetAttackDefinition(weapon);

                if (attack != null)
                {
                    lethalDmg = attack.GetTotalDamage();
                }
                return(lethalDmg);
            }

            float ApproximateStunDamage(ItemComponent weapon, Attack attack)
            {
                // Try to reduce the priority using the actual damage values and status effects.
                // This is an approximation, because we can't check the status effect conditions here.
                // The result might be incorrect if there is a high stun effect that's only applied in certain conditions.
                var statusEffects = attack.StatusEffects.Where(se => !se.HasConditions && se.type == ActionType.OnUse && se.HasRequiredItems(character));

                if (weapon.statusEffectLists != null && weapon.statusEffectLists.TryGetValue(ActionType.OnUse, out List <StatusEffect> hitEffects))
                {
                    statusEffects = statusEffects.Concat(hitEffects);
                }
                float afflictionsStun = attack.Afflictions.Keys.Sum(a => a.Identifier == "stun" ? a.Strength : 0);
                float effectsStun     = statusEffects.None() ? 0 : statusEffects.Max(se =>
                {
                    float stunAmount   = 0;
                    var stunAffliction = se.Afflictions.Find(a => a.Identifier == "stun");
                    if (stunAffliction != null)
                    {
                        stunAmount = stunAffliction.Strength;
                    }
                    return(stunAmount);
                });

                return(attack.Stun + afflictionsStun + effectsStun);
            }
        }