private void FindAvailableFights()
    {
        var unitsPositionsEntities = unitsPositions.Entities
                                     .Where(e => e.IsNotNullAndAlive() &&
                                            e.Get <MovementComponent>().IsObjectAlive);
        var enemyUnitsPositionsEntities = enemyUnits.Entities
                                          .Where(e => e.IsNotNullAndAlive() &&
                                                 e.Get <MovementComponent>().IsObjectAlive);

        foreach (var allyUnit in unitsPositionsEntities)
        {
            foreach (var enemyUnit in enemyUnitsPositionsEntities)
            {
                var allyUnitAttackComponent    = allyUnit.Get <AttackComponent>();
                var allyUnitMovementComponent  = allyUnit.Get <MovementComponent>();
                var enemyUnitMovementComponent = enemyUnit.Get <MovementComponent>();

                if (AttackHelper.CanAttack(
                        allyUnitAttackComponent,
                        allyUnitMovementComponent,
                        enemyUnitMovementComponent))
                {
                    AttackHelper.CreateAttackEvent(allyUnit, enemyUnit);
                    break;
                }
            }
        }
    }
Ejemplo n.º 2
0
 public Attack(string url, string mode, int time, int id)
 {
     Mode = AttackHelper.StringToAttackType(mode);
     Url  = url;
     Time = time;
     Id   = id;
 }
Ejemplo n.º 3
0
    protected void doSkill(SkillItemUIData skillData)
    {
        SkillType type = skillData.skillType;

        switch (type)
        {
        case SkillType.normal:    //普通技能 距离 水平角度  垂直角度 //AOI管理实体视野周围的所有实体 方便获取一定范围内的实体 这里直接拿所有实体
            List <BaseEntity> targetLst = EntityMgr.Instance.getEntityByType(EntityType.monster);
            AttackInfo        info      = new AttackInfo(skillData.atkRange, skillData.horAngle, skillData.verAngle);
            if (targetLst != null && targetLst.Count > 0)
            {
                for (int i = 0; i < targetLst.Count; i++)
                {
                    bool isAttacked = AttackHelper.isAttacked(this.dyAgent, targetLst[i], info);
                    if (isAttacked)
                    {
                        DamageData dt = new DamageData();
                        dt.casterId = this.dyAgent.UID;
                        dt.targetId = targetLst[i].UID;
                        dt.atkType  = skillData.atkType;
                        dt.hitDis   = skillData.hitDis;
                        dt.damage   = skillData.skillDamage;
                        targetLst[i].onDamage(dt);
                    }
                }
            }
            this.nextAttackTime = -1;
            break;

        case SkillType.bullet:    //子弹技能 由子弹碰撞做伤害检测
            BulletFactroy.createBullet(this.dyAgent, skillData.skillBulletId);
            this.nextAttackTime = -1;
            break;
        }
    }
Ejemplo n.º 4
0
    private void Attack()
    {
        var attackingUnitsEntities = attackingUnits.Entities
                                     .Where(u => u.IsNotNullAndAlive())
                                     .Take(attackingUnits.GetEntitiesCount());

        foreach (var unit in attackingUnitsEntities)
        {
            var attackingComponent    = unit.Get <AttackingComponent>();
            var unitAttackComponent   = unit.Get <AttackComponent>();
            var unitMovementComponent = unit.Get <MovementComponent>();

            var targetUnit = attackingComponent.TargetEntity;
            if (!targetUnit.IsNotNullAndAlive())
            {
                AttackHelper.StopAttack(unit);
                return;
            }

            var targetMovementComponent = targetUnit.Get <MovementComponent>();

            if (targetMovementComponent == null || !targetMovementComponent.IsObjectAlive)
            {
                AttackHelper.StopAttack(unit);
                return;
            }


            if (attackingComponent.TargetEntity.Get <UnitComponent>() != null &&
                AttackHelper.CanAttack(unitAttackComponent, unitMovementComponent, targetMovementComponent))
            {
                unitAttackComponent.LastAttackTime = DateTime.Now;
                ServerClient.Communication.AttackSender.attacks.Add(
                    new AttackInfo(
                        unit.Get <UnitComponent>().Guid,
                        attackingComponent.TargetEntity.Get <UnitComponent>().Guid,
                        (int)unitAttackComponent.AttackDamage));
            }
            if (attackingComponent.TargetEntity.Get <BuildingComponent>() != null)
            {
                var enemyBuildTransform = attackingComponent.TargetEntity.Get <BuildingComponent>().Object.transform;
                var attackRange         = Math.Max(enemyBuildTransform.lossyScale.x, enemyBuildTransform.lossyScale.z) * 5;
                if (attackingComponent.TargetEntity.Get <BuildingComponent>().Tag == BuildingTag.Base)
                {
                    attackRange *= 6;
                }

                if (AttackHelper.CanBuildingAttack(unitAttackComponent,
                                                   unitMovementComponent,
                                                   targetMovementComponent,
                                                   (int)attackRange))
                {
                    unitAttackComponent.LastAttackTime = DateTime.Now;
                    ServerClient.Communication.AttackSender.attacks.Add(new AttackInfo(unit.Get <UnitComponent>().Guid,
                                                                                       attackingComponent.TargetEntity.Get <BuildingComponent>().Guid,
                                                                                       (int)unitAttackComponent.AttackDamage));
                }
            }
        }
    }
Ejemplo n.º 5
0
    // We're just doing straight damage here
    public override void DealEffects(Unit target, Unit source)
    {
        if (target != null)
        {
            //deal core damage to central target
            target.ChangeHealth((GetDamage() * (-1)), source, this);
        }
        Vector2Int origin = source.GetMapPosition();

        origin += MapMath.DirToRelativeLoc(source.GetDirection());
        List <Vector2Int> area = AttackHelper.GetCircleAOE(origin, source.GetDirection(), GetRange());

        //skip first one(center)
        for (int i = 0; i < area.Count; i++)
        {
            if (area[i] == source.GetMapPosition())
            {
                continue;
            }
            Unit searchResult = source.globalPositionalData.SearchLocation(area[i]);
            if (searchResult != null)
            {
                searchResult.ChangeHealth((GetDamage() * -1) / 3, source, this);
                Vector2Int diff = searchResult.GetMapPosition() - origin;
                // AttackHelper.DisplaceUnit(searchResult, source, this, 1, MapMath.LocToDirection(diff));
            }
        }
        source.ChangeHealth((GetDamage() * (-1)), source, this);
    }
Ejemplo n.º 6
0
 public void Setup()
 {
     helper               = new AttackHelper();
     damageHelper         = new DamageHelper();
     collectionSelector   = GetNewInstanceOf <ICollectionSelector>();
     featsSelector        = GetNewInstanceOf <IFeatsSelector>();
     creatureDataSelector = GetNewInstanceOf <ICreatureDataSelector>();
 }
Ejemplo n.º 7
0
        private Dictionary <Element, int> GenerateDamage(Player player, IWeaponItem weapon)
        {
            var weaponDamage = weapon.GenerateDamage();

            foreach (var pair in weaponDamage.ToArray())
            {
                weaponDamage[pair.Key] = AttackHelper.CalculateDamage(pair.Value, pair.Key, player);
            }

            return(weaponDamage);
        }
Ejemplo n.º 8
0
 public override void DealEffects(Unit target, Unit source)
 {
     if (target != null)
     {
         Vector2Int origin = target.GetMapPosition();
         target.ChangeHealth((GetDamage() * (-1)), source, this);
         foreach (Vector2Int tile in MapMath.GetNeighbors(origin).Keys)
         {
             Unit searchResult = source.globalPositionalData.SearchLocation(tile);
             if (searchResult != null)
             {
                 Vector2Int diff = tile - origin;
                 AttackHelper.DisplaceUnit(searchResult, source, this, 1, MapMath.LocToDirection(diff));
             }
         }
     }
 }
Ejemplo n.º 9
0
    public override void DealEffects(Unit target, Unit source)
    {
        int pushback = 5;

        if (target != null)
        {
            target.ChangeHealth((GetDamage() * (-1)), source, this);

            Vector2Int diff    = target.GetMapPosition() - source.GetMapPosition();
            Vector2Int absDiff = new Vector2Int(Mathf.Abs(diff.x), Mathf.Abs(diff.y));
            Debug.Assert(source.GetDirection() != Direction.NO_DIR);
            Vector2Int newLocation = source.GetMapPosition();
            //Debug.Log(newLocation);
            switch (source.GetDirection())
            {
            case Direction.N:
                newLocation = new Vector2Int(source.GetMapPosition().x, source.GetMapPosition().y + (absDiff.y - 1));
                break;

            case Direction.S:
                newLocation = new Vector2Int(source.GetMapPosition().x, source.GetMapPosition().y - (absDiff.y - 1));
                break;

            case Direction.W:
                newLocation = new Vector2Int(source.GetMapPosition().x - (absDiff.x - 1), source.GetMapPosition().y);
                break;

            case Direction.E:
                newLocation = new Vector2Int(source.GetMapPosition().x + (absDiff.x - 1), source.GetMapPosition().y);
                break;
            }
            //Debug.Log(newLocation);
            //check new location for issues, if so, stay at current loc
            // ********************************** source.Move(newLocation.x, newLocation.y, MovementType.DASH);
            AttackHelper.DisplaceUnit(target, source, this, pushback, source.GetDirection());
        }
        else
        {
            Vector2Int newLocation = source.GetMapPosition() + (MapMath.DirToRelativeLoc(source.GetDirection()) * pushback);
            // ********************************** source.Move(newLocation.x, newLocation.y, MovementType.DASH);
        }
    }
Ejemplo n.º 10
0
        private void CalculateDamages(Mech attacker, AbstractActor target)
        {
            Mod.MeleeLog.Info?.Write($"Calculating KICK damage for attacker: {CombatantUtils.Label(attacker)} @ {attacker.tonnage} tons " +
                                     $"vs. target: {CombatantUtils.Label(target)}");

            float damage = attacker.KickDamage();

            // Adjust damage for any target resistance
            damage = target.ApplyKickDamageReduction(damage);

            this.TargetDamageClusters = AttackHelper.CreateDamageClustersWithExtraAttacks(attacker, damage, ModStats.KickExtraHitsCount);
            StringBuilder sb = new StringBuilder(" - Target damage clusters: ");

            foreach (float cluster in this.TargetDamageClusters)
            {
                sb.Append(cluster);
                sb.Append(", ");
            }
            Mod.MeleeLog.Info?.Write(sb.ToString());
        }
        private void CalculateDamages(Mech attacker, AbstractActor target)
        {
            Mod.MeleeLog.Info?.Write($"Calculating PHYSICAL WEAPON damage for attacker: {CombatantUtils.Label(attacker)} " +
                                     $"vs. target: {CombatantUtils.Label(target)}");

            float damage = attacker.PhysicalWeaponDamage();

            damage = target.ApplyPhysicalWeaponDamageReduction(damage);

            // Target damage applies as a single modifier
            this.TargetDamageClusters = AttackHelper.CreateDamageClustersWithExtraAttacks(attacker, damage, ModStats.PhysicalWeaponExtraHitsCount);
            StringBuilder sb = new StringBuilder(" - Target damage clusters: ");

            foreach (float cluster in this.TargetDamageClusters)
            {
                sb.Append(cluster);
                sb.Append(", ");
            }
            Mod.MeleeLog.Info?.Write(sb.ToString());
        }
Ejemplo n.º 12
0
        public static AttackSelection From(string rawData)
        {
            var helper = new AttackHelper();
            var data   = helper.ParseEntry(rawData);

            var selection = new AttackSelection();

            selection.IsMelee               = Convert.ToBoolean(data[DataIndexConstants.AttackData.IsMeleeIndex]);
            selection.IsNatural             = Convert.ToBoolean(data[DataIndexConstants.AttackData.IsNaturalIndex]);
            selection.IsPrimary             = Convert.ToBoolean(data[DataIndexConstants.AttackData.IsPrimaryIndex]);
            selection.IsSpecial             = Convert.ToBoolean(data[DataIndexConstants.AttackData.IsSpecialIndex]);
            selection.Name                  = data[DataIndexConstants.AttackData.NameIndex];
            selection.DamageEffect          = data[DataIndexConstants.AttackData.DamageEffectIndex];
            selection.DamageBonusMultiplier = Convert.ToDouble(data[DataIndexConstants.AttackData.DamageBonusMultiplierIndex]);
            selection.FrequencyQuantity     = Convert.ToInt32(data[DataIndexConstants.AttackData.FrequencyQuantityIndex]);
            selection.FrequencyTimePeriod   = data[DataIndexConstants.AttackData.FrequencyTimePeriodIndex];
            selection.Save                  = data[DataIndexConstants.AttackData.SaveIndex];
            selection.SaveAbility           = data[DataIndexConstants.AttackData.SaveAbilityIndex];
            selection.AttackType            = data[DataIndexConstants.AttackData.AttackTypeIndex];
            selection.SaveDcBonus           = Convert.ToInt32(data[DataIndexConstants.AttackData.SaveDcBonusIndex]);

            var damageHelper  = new DamageHelper();
            var damageEntries = damageHelper.ParseEntries(data[DataIndexConstants.AttackData.DamageDataIndex]);

            foreach (var damageData in damageEntries)
            {
                var damage = new Damage
                {
                    Roll      = damageData[DataIndexConstants.AttackData.DamageData.RollIndex],
                    Type      = damageData[DataIndexConstants.AttackData.DamageData.TypeIndex],
                    Condition = damageData[DataIndexConstants.AttackData.DamageData.ConditionIndex],
                };

                selection.Damages.Add(damage);
            }

            return(selection);
        }
Ejemplo n.º 13
0
        public static void Prefix(MechFallSequence __instance)
        {
            Mod.Log.Trace?.Write("MFS:OnComplete - entered.");
            int damagePointsTT = (int)Math.Ceiling(__instance.OwningMech.tonnage / 10f);

            Mod.Log.Debug?.Write($"Actor: {CombatantUtils.Label(__instance.OwningMech)} will suffer {damagePointsTT} TT damage points.");

            // Check for any pilot skill damage reduction
            float damageReduction = 1.0f - __instance.OwningMech.PilotCheckMod(Mod.Config.Piloting.DFAReductionMulti);
            float reducedDamage   = (float)Math.Max(0f, Math.Floor(damageReduction * damagePointsTT));

            Mod.Log.Debug?.Write($" Reducing TT fall damage from: {damagePointsTT} by {damageReduction:P1} to {reducedDamage}");

            List <float> locationDamage = new List <float>();

            while (damagePointsTT >= 5)
            {
                locationDamage.Add(5 * Mod.Config.Piloting.FallingDamagePerTenTons);
                damagePointsTT -= 5;
            }
            if (damagePointsTT > 0)
            {
                locationDamage.Add(damagePointsTT * Mod.Config.Piloting.FallingDamagePerTenTons);
            }

            Mod.Log.Info?.Write($"FALLING DAMAGE: TT damage: {damagePointsTT} => {damagePointsTT * Mod.Config.Piloting.FallingDamagePerTenTons} falling damage to actor: {CombatantUtils.Label(__instance.OwningMech)}");

            try
            {
                (Weapon melee, Weapon dfa)fakeWeapons = ModState.GetFakedWeapons(__instance.OwningMech);
                AttackHelper.CreateImaginaryAttack(__instance.OwningMech, fakeWeapons.melee, __instance.OwningMech, __instance.SequenceGUID, locationDamage.ToArray(), DamageType.KnockdownSelf, MeleeAttackType.NotSet);
            }
            catch (Exception e)
            {
                Mod.Log.Error?.Write(e, "FAILED TO APPLY FALL DAMAGE");
            }
        }
Ejemplo n.º 14
0
        private StyledLine[] GetWeaponDetails(IWeaponItem weapon)
        {
            var result = new List <StyledLine>
            {
                new StyledLine
                {
                    $"Accuracy: {weapon.Accuracy + Player.AccuracyBonus}%"
                },
                StyledLine.Empty
            };

            foreach (Element element in Enum.GetValues(typeof(Element)))
            {
                var maxDamage = WeaponItem.GetMaxDamage(weapon, element);
                var minDamage = WeaponItem.GetMinDamage(weapon, element);

                maxDamage = AttackHelper.CalculateDamage(maxDamage, element, Player);
                minDamage = AttackHelper.CalculateDamage(minDamage, element, Player);

                if (maxDamage == 0 && minDamage == 0)
                {
                    continue;
                }

                var damageLine = new StyledLine
                {
                    new StyledString($"{TextHelper.GetElementName(element)}",
                                     TextHelper.GetElementColor(element)),
                    " Damage: ",
                    $"{minDamage} - {maxDamage}"
                };

                result.Add(damageLine);
            }

            return(result.ToArray());
        }
Ejemplo n.º 15
0
 // Use this for initialization
 void Start()
 {
     _attackHelper = GetComponent <AttackHelper>();
 }
Ejemplo n.º 16
0
 static void Postfix(AttackDirection from, ref Dictionary <ArmorLocation, int> __result)
 {
     if (ModState.ForceDamageTable == DamageTable.PUNCH)
     {
         Mod.Log.Info?.Write($"Attack against MECH will use the PUNCH damage table");
         __result = new Dictionary <ArmorLocation, int>();
         if (from == AttackDirection.FromLeft)
         {
             __result.Add(ArmorLocation.LeftTorso, 34); // 2 locations
             __result.Add(ArmorLocation.CenterTorso, 16);
             __result.Add(ArmorLocation.LeftArm, 34);   // 2 locations
             __result.Add(ArmorLocation.Head, 16);
         }
         else if (from == AttackDirection.FromBack)
         {
             __result.Add(ArmorLocation.LeftArm, 17);
             __result.Add(ArmorLocation.LeftTorsoRear, 17);
             __result.Add(ArmorLocation.CenterTorsoRear, 16);
             __result.Add(ArmorLocation.RightTorsoRear, 17);
             __result.Add(ArmorLocation.RightArm, 17);
             __result.Add(ArmorLocation.Head, 16);
         }
         else if (from == AttackDirection.FromRight)
         {
             __result.Add(ArmorLocation.RightTorso, 34); // 2 locations
             __result.Add(ArmorLocation.CenterTorso, 16);
             __result.Add(ArmorLocation.RightArm, 34);   // 2 locations
             __result.Add(ArmorLocation.Head, 16);
         }
         else
         {
             __result.Add(ArmorLocation.LeftArm, 17);
             __result.Add(ArmorLocation.LeftTorso, 17);
             __result.Add(ArmorLocation.CenterTorso, 16);
             __result.Add(ArmorLocation.RightTorso, 17);
             __result.Add(ArmorLocation.RightArm, 17);
             __result.Add(ArmorLocation.Head, 16);
         }
     }
     else if (ModState.ForceDamageTable == DamageTable.KICK)
     {
         Mod.Log.Info?.Write($"Attack against MECH will use the KICK damage table");
         __result = new Dictionary <ArmorLocation, int>();
         __result.Add(ArmorLocation.LeftLeg, 50);
         __result.Add(ArmorLocation.RightLeg, 50);
     }
     else if (ModState.ForceDamageTable == DamageTable.SWARM)
     {
         Mod.Log.Info?.Write($"Attack against MECH will use the SWARM damage table");
         __result = new Dictionary <ArmorLocation, int>();
         __result.Add(AttackHelper.GetSwarmLocationForMech(), 100);
         //__result.Add(ArmorLocation.LeftTorso, 17);
         //__result.Add(ArmorLocation.LeftTorsoRear, 17);
         //__result.Add(ArmorLocation.CenterTorso, 16);
         //__result.Add(ArmorLocation.CenterTorsoRear, 16);
         //__result.Add(ArmorLocation.RightTorso, 17);
         //__result.Add(ArmorLocation.RightTorsoRear, 17);
         //__result.Add(ArmorLocation.Head, 16);
     }
     else
     {
         return;
     }
 }
Ejemplo n.º 17
0
        static void Postfix(AttackDirection from, bool log, ref Dictionary <VehicleChassisLocations, int> __result)
        {
            if (ModState.ForceDamageTable == DamageTable.PUNCH)
            {
                Mod.Log.Info?.Write($"Attack against VEHICLE will use the PUNCH damage table");
                __result = new Dictionary <VehicleChassisLocations, int>();
                if (from == AttackDirection.FromLeft)
                {
                    __result.Add(VehicleChassisLocations.Turret, 40);
                    __result.Add(VehicleChassisLocations.Left, 40);
                    __result.Add(VehicleChassisLocations.Front, 8);
                    __result.Add(VehicleChassisLocations.Rear, 8);
                }
                else if (from == AttackDirection.FromBack)
                {
                    __result.Add(VehicleChassisLocations.Turret, 40);
                    __result.Add(VehicleChassisLocations.Rear, 40);
                    __result.Add(VehicleChassisLocations.Left, 8);
                    __result.Add(VehicleChassisLocations.Right, 8);
                }
                else if (from == AttackDirection.FromRight)
                {
                    __result.Add(VehicleChassisLocations.Turret, 40);
                    __result.Add(VehicleChassisLocations.Right, 40);
                    __result.Add(VehicleChassisLocations.Front, 8);
                    __result.Add(VehicleChassisLocations.Rear, 8);
                }
                else if (from == AttackDirection.FromTop)
                {
                    __result.Add(VehicleChassisLocations.Turret, 40);
                    __result.Add(VehicleChassisLocations.Front, 8);
                    __result.Add(VehicleChassisLocations.Left, 8);
                    __result.Add(VehicleChassisLocations.Right, 8);
                }
                else
                {
                    __result.Add(VehicleChassisLocations.Turret, 40);
                    __result.Add(VehicleChassisLocations.Front, 40);
                    __result.Add(VehicleChassisLocations.Left, 8);
                    __result.Add(VehicleChassisLocations.Right, 8);
                }
            }
            else if (ModState.ForceDamageTable == DamageTable.KICK)
            {
                Mod.Log.Info?.Write($"Attack against VEHICLE will use the KICK damage table");
                __result = new Dictionary <VehicleChassisLocations, int>();
                if (from == AttackDirection.FromLeft)
                {
                    __result.Add(VehicleChassisLocations.Turret, 4);
                    __result.Add(VehicleChassisLocations.Left, 40);
                    __result.Add(VehicleChassisLocations.Front, 8);
                    __result.Add(VehicleChassisLocations.Rear, 8);
                }
                else if (from == AttackDirection.FromBack)
                {
                    __result.Add(VehicleChassisLocations.Turret, 4);
                    __result.Add(VehicleChassisLocations.Rear, 40);
                    __result.Add(VehicleChassisLocations.Left, 8);
                    __result.Add(VehicleChassisLocations.Right, 8);
                }
                else if (from == AttackDirection.FromRight)
                {
                    __result.Add(VehicleChassisLocations.Turret, 4);
                    __result.Add(VehicleChassisLocations.Right, 40);
                    __result.Add(VehicleChassisLocations.Front, 8);
                    __result.Add(VehicleChassisLocations.Rear, 8);
                }
                else if (from == AttackDirection.FromTop)
                {
                    __result.Add(VehicleChassisLocations.Turret, 40);
                    __result.Add(VehicleChassisLocations.Front, 8);
                    __result.Add(VehicleChassisLocations.Left, 8);
                    __result.Add(VehicleChassisLocations.Right, 8);
                }
                else
                {
                    __result.Add(VehicleChassisLocations.Turret, 4);
                    __result.Add(VehicleChassisLocations.Front, 40);
                    __result.Add(VehicleChassisLocations.Left, 8);
                    __result.Add(VehicleChassisLocations.Right, 8);
                }
            }
            else if (ModState.ForceDamageTable == DamageTable.SWARM)
            {
                Mod.Log.Info?.Write($"Attack against VEHICLE will use the SWARM damage table");
                __result = new Dictionary <VehicleChassisLocations, int>();
                __result.Add(AttackHelper.GetSwarmLocationForVehicle(), 100);

                //__result.Add(VehicleChassisLocations.Turret, 40);
                //__result.Add(VehicleChassisLocations.Left, 16);
                //__result.Add(VehicleChassisLocations.Front, 8);
                //__result.Add(VehicleChassisLocations.Right, 16);
                //__result.Add(VehicleChassisLocations.Rear, 16);
            }
            else
            {
                return;
            }
        }
Ejemplo n.º 18
0
 public override List <Vector2Int> GetAreaOfEffect(Vector2Int source, Direction direction)
 {
     return(AttackHelper.GetLineAOE(source, direction, GetRange()));
 }
Ejemplo n.º 19
0
    public override List <Vector2Int> GetAreaOfEffect(Vector2Int source, Direction direction)
    {
        List <Vector2Int> result = AttackHelper.GetTShapedAOE(source, direction, GetRange());

        return(result);
    }
Ejemplo n.º 20
0
 public void Setup()
 {
     selection    = new AttackSelection();
     attackHelper = new AttackHelper();
     damageHelper = new DamageHelper();
 }
Ejemplo n.º 21
0
 public void Setup()
 {
     helper       = new AttackHelper();
     damageHelper = new DamageHelper();
 }
        public override IEnumerable <int> AttackRoutine()
        {
            var allDeployElements = Deploy.GetTroops();

            List <Point> redPoints = new List <Point>();

            foreach (var s in AttackHelper.FindRedPoints(redPoints))
            {
                yield return(s);
            }

            var leftCorner = DeployHelper.DeployPointALeft;
            var topCorner  = DeployHelper.DeployPointATop;

            double tankPaddingFraction = 0.35;
            var    tankDeployA         = leftCorner.Lerp(topCorner, tankPaddingFraction);
            var    tankDeployB         = leftCorner.Lerp(topCorner, 1 - tankPaddingFraction);

            double wallBreakerFraction = 0.43;
            var    wallBreakDeployA    = leftCorner.Lerp(topCorner, wallBreakerFraction);
            var    wallBreakDeployB    = leftCorner.Lerp(topCorner, 1 - wallBreakerFraction);

            double damageFraction     = 0.15;
            var    damageDeployPointA = leftCorner.Lerp(topCorner, damageFraction);
            var    damageDeployPointB = leftCorner.Lerp(topCorner, 1 - damageFraction);

            // We want to drag our deploy points to the closest redline point
            // For that we get the vector from left to top, then we get its orthogonal vector (the one of both that points more towards the center)
            // then we can order the redline points. we want the one whos direction is closest to the orthogonal vector.
            var vecLeftTop = topCorner.Subtract(leftCorner).Normalize();
            var ortho      = GetOrthogonalDirection(tankDeployA, new Point((int)(vecLeftTop.Item1 * 30), (int)(vecLeftTop.Item2 * 30)), new PointF(0.5f, 0.5f).ToAbsolute());

            tankDeployA = GetBestRedlinePoint(redPoints, tankDeployA, ortho);
            tankDeployB = GetBestRedlinePoint(redPoints, tankDeployB, ortho);

            wallBreakDeployA = GetBestRedlinePoint(redPoints, wallBreakDeployA, ortho);
            wallBreakDeployB = GetBestRedlinePoint(redPoints, wallBreakDeployB, ortho);


            var attackLine = DeployHelper.GetPointsForLine(damageDeployPointA, damageDeployPointB, 30).ToArray();

            for (int i = 0; i < attackLine.Length; i++)
            {
                attackLine[i] = GetBestRedlinePoint(redPoints, attackLine[i], ortho);
            }
            attackLine = attackLine.Distinct().ToArray();


            var validUnits = allDeployElements.Where(u => u.UnitData != null).ToArray();
            var clanTroops = allDeployElements.FirstOrDefault(u => u.ElementType == DeployElementType.ClanTroops);
            //Func<DeployElement, bool> isAir = e => e.UnitData.UnitType == UnitType.Air;
            Func <DeployElement, bool> isBalloon = e => e.UnitData.NameSimple.Contains("balloon");
            Func <DeployElement, bool> isMinion  = e => e.UnitData.NameSimple.Contains("minion");

            var tankUnits        = validUnits.Where(u => u.UnitData.AttackType == AttackType.Tank).ToArray();
            var wallBreakerUnits = validUnits.Where(u => u.UnitData.AttackType == AttackType.Wallbreak).ToArray();
            var attackUnits      = validUnits.Where(u => u.UnitData.AttackType == AttackType.Damage && !isMinion(u)).ToArray();

            if (clanTroops != null && UserSettings.UseClanTroops)
            {
                attackUnits = attackUnits.Concat(new[] { clanTroops }).ToArray();
            }
            var healUnits    = validUnits.Where(u => u.UnitData.AttackType == AttackType.Heal).ToArray();
            var balloonUnits = validUnits.Where(isBalloon).ToArray();
            var minionUnits  = validUnits.Where(isMinion).ToArray();

            // Deploy tank units
            if (tankUnits.Any())
            {
                Logger.Debug($"{tankUnits.Length} tanking element{(tankUnits.Length > 1 ? "s" : "")} available to deploy.");
                foreach (var s in DeployUnits(tankUnits, new[] { tankDeployA, tankDeployB }, 20))
                {
                    yield return(s);
                }
                yield return(2000);
            }

            // Deploy wallbreakers
            if (tankUnits.Any())
            {
                Logger.Debug("Wallbreakers available to deploy.");
                foreach (var s in DeployUnits(wallBreakerUnits, new[] { wallBreakDeployA, wallBreakDeployB }, 40))
                {
                    yield return(s);
                }
                yield return(1000);
            }

            // Check whether we got an air troopset and decide to perform an air attack or not
            var balloonCount = balloonUnits.Sum(i => i.Count);

            if (balloonCount > 10)
            {
                // Ok, we have an air troopset, so we will deploy the air units first according to different deploy rules.
                attackUnits = attackUnits.Where(u => !isBalloon(u)).ToArray();

                int spotCount = (int)Math.Ceiling(balloonCount / 4.0);
                // We want to make x spots where balloons are deployed
                var airPoints = DeployHelper.GetPointsForLine(damageDeployPointA, damageDeployPointB, spotCount);
                for (int i = 0; i < airPoints.Count; i++)
                {
                    airPoints[i] = GetBestRedlinePoint(redPoints, airPoints[i], ortho);
                }
                airPoints = airPoints.Distinct().ToList();

                // Deploy those air units
                foreach (var s in DeployUnits(balloonUnits, airPoints.ToArray(), firstCycleYield: 1000))
                {
                    yield return(s);
                }
            }

            // Deploy atackers
            if (attackUnits.Any())
            {
                Logger.Debug($"{attackUnits.Length} attacking element{(attackUnits.Length > 1 ? "s" : "")} available to deploy.");
                foreach (var s in DeployUnits(attackUnits, attackLine, 0, 5, 2500))
                {
                    yield return(s);
                }
                yield return(500);
            }

            // Minions
            if (minionUnits.Any())
            {
                foreach (var s in DeployUnits(minionUnits, attackLine))
                {
                    yield return(s);
                }
            }

            // Deploy healers
            foreach (var s in DeployUnits(healUnits, attackLine))
            {
                yield return(s);
            }

            // Deploy heroes
            var heroes = allDeployElements
                         .Where(u => (UserSettings.UseKing && u.ElementType == DeployElementType.HeroKing) ||
                                (UserSettings.UseQueen && u.ElementType == DeployElementType.HeroQueen) ||
                                (UserSettings.UseWarden && u.ElementType == DeployElementType.HeroWarden))
                         .ToList();

            if (heroes.Count > 0)
            {
                foreach (var y in DeployHeroes(heroes, attackLine))
                {
                    yield return(y);
                }
            }
        }
        static void Prefix(AIUtil __instance, AbstractActor unit, AttackType attackType, List <Weapon> weaponList,
                           ICombatant target, Vector3 attackPosition, Vector3 targetPosition, bool useRevengeBonus,
                           AbstractActor unitForBVContext)
        {
            Mech          attackingMech = unit as Mech;
            AbstractActor targetActor   = target as AbstractActor;
            Mech          targetMech    = target as Mech;

            Mod.AILog.Info?.Write("AITUIL_EDFA - entered.");

            if (attackingMech == null || targetActor == null)
            {
                return;                                               // Nothing to do
            }
            if (attackType == AttackType.Shooting || attackType == AttackType.None || attackType == AttackType.Count)
            {
                return;                                                                                                       // nothing to do
            }
            try
            {
                Mod.AILog.Info?.Write($"=== Calculating expectedDamage for {attackingMech.DistinctId()} melee attack " +
                                      $"from position: {attackPosition} against target: {target.DistinctId()} at position: {targetPosition}");
                Mod.AILog.Info?.Write($"  useRevengeBonus: {useRevengeBonus}");
                Mod.AILog.Info?.Write($"  --- weaponList:");
                if (weaponList != null)
                {
                    foreach (Weapon weapon in weaponList)
                    {
                        Mod.AILog.Info?.Write($"      {weapon?.UIName}");
                    }
                }

                bool        modifyAttack = false;
                MeleeAttack meleeAttack  = null;
                Weapon      meleeWeapon  = null;

                bool isCharge = false;
                bool isMelee  = false;
                if (attackType == AttackType.Melee && attackingMech?.Pathing?.GetMeleeDestsForTarget(targetActor)?.Count > 0)
                {
                    Mod.AILog.Info?.Write($"Modifying {attackingMech.DistinctId()}'s melee attack damage for utility");

                    // Create melee options
                    MeleeState meleeState = ModState.AddorUpdateMeleeState(attackingMech, attackPosition, targetActor);
                    if (meleeState != null)
                    {
                        meleeAttack = meleeState.GetHighestDamageAttackForUI();
                        ModState.AddOrUpdateSelectedAttack(attackingMech, meleeAttack);
                        if (meleeAttack is ChargeAttack)
                        {
                            isCharge = true;
                        }

                        meleeWeapon  = attackingMech.MeleeWeapon;
                        modifyAttack = true;
                        isMelee      = true;
                    }
                }

                bool isDFA = false;
                if (attackType == AttackType.DeathFromAbove && attackingMech?.JumpPathing?.GetDFADestsForTarget(targetActor)?.Count > 0)
                {
                    Mod.AILog.Info?.Write($"Modifying {attackingMech.DistinctId()}'s DFA attack damage for utility");

                    // Create melee options
                    MeleeState meleeState = ModState.AddorUpdateMeleeState(attackingMech, attackPosition, targetActor);
                    if (meleeState != null)
                    {
                        meleeAttack = meleeState.DFA;
                        ModState.AddOrUpdateSelectedAttack(attackingMech, meleeAttack);

                        meleeWeapon  = attackingMech.DFAWeapon;
                        modifyAttack = true;
                        isDFA        = true;
                    }
                }

                // No pathing dests for melee or DFA - skip
                if (!isMelee && !isDFA)
                {
                    return;
                }

                if (modifyAttack && meleeAttack == null || !meleeAttack.IsValid)
                {
                    Mod.AILog.Info?.Write($"Failed to find a valid melee state, marking melee weapons as 1 damage.");
                    meleeWeapon.StatCollection.Set <float>(ModStats.HBS_Weapon_DamagePerShot, 0);
                    meleeWeapon.StatCollection.Set <float>(ModStats.HBS_Weapon_Instability, 0);
                    return;
                }

                if (modifyAttack && meleeAttack != null && meleeAttack.IsValid)
                {
                    Mod.AILog.Info?.Write($"Evaluating utility against state: {meleeAttack.Label}");
                    // Set the DFA weapon's damage to our expected damage
                    float totalDamage = meleeAttack.TargetDamageClusters.Sum();
                    Mod.AILog.Info?.Write($" - totalDamage: {totalDamage}");

                    // Check to see if the attack will unsteady a target
                    float evasionBreakUtility = 0f;
                    if (targetMech != null && targetMech.EvasivePipsCurrent > 0 &&
                        (meleeAttack.OnTargetMechHitForceUnsteady || AttackHelper.WillUnsteadyTarget(meleeAttack.TargetInstability, targetMech))
                        )
                    {
                        // Target will lose their evasion pips
                        evasionBreakUtility = targetMech.EvasivePipsCurrent * Mod.Config.Melee.AI.EvasionPipRemovedUtility;
                        Mod.AILog.Info?.Write($"  Adding {evasionBreakUtility} virtual damage to EV from " +
                                              $"evasivePips: {targetMech.EvasivePipsCurrent} x bonusDamagePerPip: {Mod.Config.Melee.AI.EvasionPipRemovedUtility}");
                    }

                    float knockdownUtility = 0f;
                    if (targetMech != null && targetMech.pilot != null &&
                        AttackHelper.WillKnockdownTarget(meleeAttack.TargetInstability, targetMech, meleeAttack.OnTargetMechHitForceUnsteady))
                    {
                        float centerTorsoArmorAndStructure = targetMech.GetMaxArmor(ArmorLocation.CenterTorso) + targetMech.GetMaxStructure(ChassisLocations.CenterTorso);
                        if (AttackHelper.WillInjuriesKillTarget(targetMech, 1))
                        {
                            knockdownUtility = centerTorsoArmorAndStructure * Mod.Config.Melee.AI.PilotInjuryMultiUtility;
                            Mod.AILog.Info?.Write($"  Adding {knockdownUtility} virtual damage to EV from " +
                                                  $"centerTorsoArmorAndStructure: {centerTorsoArmorAndStructure} x injuryMultiUtility: {Mod.Config.Melee.AI.PilotInjuryMultiUtility}");
                        }
                        else
                        {
                            // Attack won't kill, so only apply a fraction equal to the totalHeath
                            float injuryFraction = (targetMech.pilot.TotalHealth - 1) - (targetMech.pilot.Injuries + 1);
                            knockdownUtility = (centerTorsoArmorAndStructure * Mod.Config.Melee.AI.PilotInjuryMultiUtility) / injuryFraction;
                            Mod.AILog.Info?.Write($"  Adding {knockdownUtility} virtual damage to EV from " +
                                                  $"(centerTorsoArmorAndStructure: {centerTorsoArmorAndStructure} x injuryMultiUtility: {Mod.Config.Melee.AI.PilotInjuryMultiUtility}) " +
                                                  $"/ injuryFraction: {injuryFraction}");
                        }
                    }

                    // Check to see how much evasion loss the attacker will have
                    //  use current pips + any pips gained from movement (charge)
                    float distance      = (attackPosition + unit.CurrentPosition).magnitude;
                    int   newPips       = unit.GetEvasivePipsResult(distance, isDFA, isCharge, true);
                    int   normedNewPips = (unit.EvasivePipsCurrent + newPips) > unit.StatCollection.GetValue <int>("MaxEvasivePips") ?
                                          unit.StatCollection.GetValue <int>("MaxEvasivePips") : (unit.EvasivePipsCurrent + newPips);
                    float selfEvasionDamage = 0f;
                    if (meleeAttack.UnsteadyAttackerOnHit || meleeAttack.UnsteadyAttackerOnMiss)
                    {
                        // TODO: Should evaluate chance to hit, and apply these partial damage based upon success chances
                        selfEvasionDamage = normedNewPips * Mod.Config.Melee.AI.EvasionPipLostUtility;
                        Mod.AILog.Info?.Write($"  Reducing virtual damage by {selfEvasionDamage} due to potential loss of {normedNewPips} pips.");
                    }

                    // Check to see how much damage the attacker will take
                    float selfDamage = 0f;
                    if (meleeAttack.AttackerDamageClusters.Length > 0)
                    {
                        selfDamage = meleeAttack.AttackerDamageClusters.Sum();
                        Mod.AILog.Info?.Write($"  Reducing virtual damage by {selfDamage} due to attacker damage on attack.");
                    }

                    float virtualDamage = totalDamage + evasionBreakUtility + knockdownUtility - selfEvasionDamage - selfDamage;
                    Mod.AILog.Info?.Write($"  Virtual damage calculated as {virtualDamage} = " +
                                          $"totalDamage: {totalDamage} + evasionBreakUtility: {evasionBreakUtility} + knockdownUtility: {knockdownUtility}" +
                                          $" - selfDamage: {selfDamage} - selfEvasionDamage: {selfEvasionDamage}");

                    Mod.AILog.Info?.Write($"Setting weapon: {meleeWeapon.UIName} to virtual damage: {virtualDamage} for EV calculation");
                    meleeWeapon.StatCollection.Set <float>(ModStats.HBS_Weapon_DamagePerShot, virtualDamage);
                    meleeWeapon.StatCollection.Set <float>(ModStats.HBS_Weapon_Instability, 0);

                    Mod.AILog.Info?.Write($"=== Done modifying attack!");
                }
                else
                {
                    Mod.AILog.Debug?.Write($"Attack is not melee {modifyAttack}, or melee state is invalid or null. I assume the normal AI will prevent action.");
                }
            }
            catch (Exception e)
            {
                Mod.AILog.Error?.Write(e, $"Failed to calculate melee damage for {unit.DistinctId()} using attackType {attackType} due to error!");
            }
        }
Ejemplo n.º 24
0
 public override IEnumerable <Vector2i> GetAttackableLocations(Map map, Actor actor, WeaponProperties weapon)
 {
     return(AttackHelper.GetTargetablePoints(map, actor, weapon.MinRange, weapon.MaxRange, true, false));
 }
        static void Prefix(MechDFASequence __instance, MessageCenterMessage message, AttackStackSequence ___meleeSequence)
        {
            Mod.Log.Trace?.Write("MMS:OMC entered.");

            AttackCompleteMessage attackCompleteMessage = message as AttackCompleteMessage;

            Mod.MeleeLog.Info?.Write($"== Resolving cluster damage, instability, and unsteady on DFA attacker: {CombatantUtils.Label(__instance.OwningMech)} and " +
                                     $"target: {CombatantUtils.Label(__instance.DFATarget)}.");
            (MeleeAttack meleeAttack, Weapon fakeWeapon)seqState = ModState.GetMeleeSequenceState(__instance.SequenceGUID);

            if (attackCompleteMessage.stackItemUID == ___meleeSequence.SequenceGUID && seqState.meleeAttack != null)
            {
                // Check to see if the target was hit
                bool targetWasHit = false;
                foreach (AttackDirector.AttackSequence attackSequence in ___meleeSequence.directorSequences)
                {
                    if (!attackSequence.attackCompletelyMissed)
                    {
                        targetWasHit = true;
                        Mod.MeleeLog.Info?.Write($" -- AttackSequence: {attackSequence.stackItemUID} hit the target.");
                    }
                    else
                    {
                        Mod.MeleeLog.Info?.Write($" -- AttackSequence: {attackSequence.stackItemUID} missed the target.");
                    }
                }

                if (__instance.OwningMech.isHasStability() && !__instance.OwningMech.IsOrWillBeProne)
                {
                    // Attacker stability and unsteady - always applies as we're always a mech
                    if ((targetWasHit && seqState.meleeAttack.UnsteadyAttackerOnHit) ||
                        (!targetWasHit && seqState.meleeAttack.UnsteadyAttackerOnMiss))
                    {
                        Mod.MeleeLog.Info?.Write(" -- Forcing attacker to become unsteady from attack!");
                        __instance.OwningMech.DumpEvasion();
                    }
                }

                // Attacker cluster damage
                if (targetWasHit && !__instance.OwningMech.IsDead)
                {
                    // Make sure we use the attackers's damage table
                    ModState.ForceDamageTable = seqState.meleeAttack.AttackerTable;
                    if (seqState.meleeAttack.AttackerDamageClusters.Length > 0)
                    {
                        try
                        {
                            Mod.MeleeLog.Info?.Write($" -- Applying {seqState.meleeAttack.AttackerDamageClusters.Sum()} damage to attacker as {seqState.meleeAttack.AttackerDamageClusters.Length} clusters.");
                            AttackHelper.CreateImaginaryAttack(__instance.OwningMech, seqState.fakeWeapon, __instance.OwningMech, __instance.SequenceGUID,
                                                               seqState.meleeAttack.AttackerDamageClusters, DamageType.Melee, MeleeAttackType.Kick);
                        }
                        catch (Exception e)
                        {
                            Mod.Log.Error?.Write(e, "FAILED TO APPLY DFA DAMAGE TO ATTACKER");
                        }
                    }
                }

                if (targetWasHit)
                {
                    // Target mech stability and unsteady
                    if (__instance.DFATarget is Mech targetMech && targetMech.isHasStability() && !targetMech.IsProne)
                    {
                        if (seqState.meleeAttack.TargetInstability != 0)
                        {
                            Mod.MeleeLog.Info?.Write($" -- Adding {seqState.meleeAttack.TargetInstability} absolute instability to target.");
                            targetMech.AddAbsoluteInstability(seqState.meleeAttack.TargetInstability, StabilityChangeSource.Attack, "-1");
                        }

                        if (seqState.meleeAttack.OnTargetMechHitForceUnsteady)
                        {
                            Mod.MeleeLog.Info?.Write(" -- Forcing target to become unsteady from attack!");
                            targetMech.DumpEvasion();
                        }
                    }

                    // Target vehicle evasion damage
                    if (__instance.DFATarget is Vehicle || __instance.DFATarget.FakeVehicle() || __instance.DFATarget.NavalUnit())
                    {
                        AbstractActor targetActor = __instance.DFATarget as AbstractActor;
                        if (seqState.meleeAttack.OnTargetVehicleHitEvasionPipsRemoved != 0 && targetActor.EvasivePipsCurrent > 0)
                        {
                            Mod.MeleeLog.Info?.Write($" -- Removing {seqState.meleeAttack.OnTargetVehicleHitEvasionPipsRemoved} from target vehicle.");
                            int modifiedPips = targetActor.EvasivePipsCurrent - seqState.meleeAttack.OnTargetVehicleHitEvasionPipsRemoved;
                            if (modifiedPips < 0)
                            {
                                modifiedPips = 0;
                            }

                            targetActor.EvasivePipsCurrent = modifiedPips;
                            SharedState.Combat.MessageCenter.PublishMessage(new EvasiveChangedMessage(targetActor.GUID, targetActor.EvasivePipsCurrent));
                        }
                    }

                    // Target cluster damage - first attack was applied through melee weapon
                    if (seqState.meleeAttack.TargetDamageClusters.Length > 1 && !__instance.DFATarget.IsDead)
                    {
                        try
                        {
                            // Make sure we use the attackers's damage table
                            ModState.ForceDamageTable = seqState.meleeAttack.TargetTable;

                            // The target already got hit by the first cluster as the weapon damage. Only add the additional hits
                            float[] clusterDamage = seqState.meleeAttack.TargetDamageClusters.SubArray(1, seqState.meleeAttack.TargetDamageClusters.Length);
                            Mod.MeleeLog.Info?.Write($" -- Applying {clusterDamage.Sum()} damage to target as {clusterDamage.Length} clusters.");
                            AttackHelper.CreateImaginaryAttack(__instance.OwningMech, seqState.fakeWeapon, __instance.DFATarget, __instance.SequenceGUID, clusterDamage,
                                                               DamageType.Melee, MeleeAttackType.DFA);
                        }
                        catch (Exception e)
                        {
                            Mod.Log.Error?.Write(e, "FAILED TO APPLY DFA DAMAGE TO TARGET");
                        }
                    }
                }

                Mod.MeleeLog.Info?.Write($"== Done.");
            }

            // Restore the attacker's DFA damage
            __instance.OwningMech.StatCollection.Set <float>(ModStats.HBS_DFA_Self_Damage, ModState.OriginalDFASelfDamage);
            __instance.OwningMech.StatCollection.Set <bool>(ModStats.HBS_DFA_Causes_Self_Unsteady, true);

            // Reset melee state
            ModState.ForceDamageTable      = DamageTable.NONE;
            ModState.OriginalDFASelfDamage = 0f;
        }