Пример #1
0
 public WeaponParams(int w, int t, float a, WeaponDamageRange rng)
 {
     weapon   = w;
     target   = t;
     accuracy = a;
     range    = rng;
 }
Пример #2
0
 public void DisplayAttack(float accuracy, WeaponDamageRange range)
 {
     gameObject.SetActive(true);
     accuracyText.transform.parent.gameObject.SetActive(true);
     shieldRangeText.transform.parent.gameObject.SetActive(true);
     hullRangeText.transform.parent.gameObject.SetActive(true);
     accuracyText.text    = accuracy.ToString("F2");
     shieldRangeText.text = range.shieldRange.x.ToString("F2") + " - " + range.shieldRange.y.ToString("F2");
     hullRangeText.text   = range.hullRange.x.ToString("F2") + " - " + range.hullRange.y.ToString("F2");
 }
Пример #3
0
    public WeaponDamageRange CalculateDamageRange(WeaponProperties weapon, float accuracy, FiringZone.Face face, float targetShield, float priorShieldDamage = 0)
    {
        WeaponDamageRange damageRange = new WeaponDamageRange();

        damage.AddModifier(properties.Profile[(int)face]);
        // target has a shield - need to factor in each weapon types effectiveness
        if (targetShield - priorShieldDamage > 0)
        {
            // a list going from most effective to least effective against shields
            foreach (var shieldPriority in DamageTypeEffect.SHIELD_PRIORITY_LIST)
            {
                Vector2Int range = weapon.Damage[shieldPriority];
                // TODO add modifiers of effect
                // THIS IS PROBLEM AREA
                // TODO doing targetDamage.shieldRange.x is being conservative
                // doing targetDamage.shieldRange.y would be greedy
                if (damageRange.shieldRange.x >= targetShield - priorShieldDamage)
                {
                    damage.BaseValue         = range.x;
                    damageRange.hullRange.x += damage.Value * DamageTypeEffect.HullEffect(shieldPriority);
                    damage.BaseValue         = range.y;
                    damageRange.hullRange.y += damage.Value * DamageTypeEffect.HullEffect(shieldPriority);
                }
                // else if (damageRange.shieldRange.y >= targetShield - targetDamage.shieldRange.x)
                // {
                //     damage.BaseValue = range.x;
                //     damageRange.shieldRange.x += damage.Value;
                //     damage.BaseValue = range.y;
                //     damageRange.hullRange.y += damage.Value;
                // }
                else
                {
                    damage.BaseValue           = range.x;
                    damageRange.shieldRange.x += damage.Value * DamageTypeEffect.ShieldEffect(shieldPriority);
                    damage.BaseValue           = range.y;
                    damageRange.shieldRange.y += damage.Value * DamageTypeEffect.ShieldEffect(shieldPriority);
                }
            }
            damageRange *= accuracy;
            damageRange.shieldRange.x = Mathf.Clamp(damageRange.shieldRange.x, 0, targetShield);
            damageRange.shieldRange.y = Mathf.Clamp(damageRange.shieldRange.y, 0, targetShield);
        }
        else
        {
            // if the target has no shield it is simply hull damage
            // hull damage of a weapon can be precomputed and cached with effectivenesses
            damage.BaseValue        = weapon.HullDamage.x;
            damageRange.hullRange.x = damage.Value;
            damage.BaseValue        = weapon.HullDamage.y;
            damageRange.hullRange.y = damage.Value;
            damageRange            *= accuracy;
        }
        damage.RemoveModifier(properties.Profile[(int)face]);
        return(damageRange);
    }
Пример #4
0
    List <Tuple <ActionType, ActionParams> > EvaluateAI()
    {
        currentAP = ShipDefaults.BASE_AP; // TODO REMOVE
        field.GenerateField();            // TODO REMOVE
        System.Text.StringBuilder str;    // TODO REMOVE

        int     weaponCnt = Weapons.Length;
        int     shipCnt   = PlayerShip.PlayerShips.Count;
        Vector3 position  = transform.position;

        // Caching the accuracy and face the ship is on
        Tuple <float, FiringZone.Face>[] targetParams = new Tuple <float, FiringZone.Face> [weaponCnt * shipCnt];
        WeaponDamageRange[] targetDamage = new WeaponDamageRange[shipCnt];
        // Things the AI will do
        // List<Tuple<ActionType, int, int>> actionList = null;
        List <Tuple <ActionType, ActionParams> > actionList = null;
        // Best action
        float maxActionValue = 0;

        for (int i = 0; i < CurrentAP; i++)
        {
            // AP after a move (ie each iteration is assuming 1 movement)
            int ap = CurrentAP - i;
            Zone.RecalculateFrustum(position, transform.rotation);

            // Calculate + cache the accuracy of a weapon to each ship and the face the ship is on from the ship
            // This is just setup
            // TODO this can be put off into the recursion loop
            for (int j = 0; j < shipCnt; j++)
            {
                Ship            target = PlayerShip.PlayerShips[j];
                FiringZone.Face face   = Zone.FrustrumFace(target.transform.position);
                if (face == FiringZone.Face.None || properties.Profile[(int)face].Value == 0)
                {
                    for (int k = 0; k < weaponCnt; k++)
                    {
                        targetParams[k * shipCnt + j] = new Tuple <float, FiringZone.Face>(0, FiringZone.Face.None);
                    }
                    Debug.Log("Skip face.");
                    continue;
                }
                System.Text.StringBuilder cacheStr = new System.Text.StringBuilder($"Ship: {j} => Face: {face}");
                targetDamage[j] = new WeaponDamageRange();
                for (int k = 0; k < weaponCnt; k++)
                {
                    if (Weapons[k].apCost > CurrentAP - i)
                    {
                        continue;
                    }
                    float acc = CalculateAccuracy(
                        Weapons[k],
                        target.transform.position,
                        target.properties.Evasion.Value,
                        position
                        );
                    // if (acc < minAccuracy) continue;
                    cacheStr.Append($" Weapon: {k} => Accuracy: {acc}");
                    targetParams[k * shipCnt + j] = new Tuple <float, FiringZone.Face>(acc, face);
                }
                Debug.Log(cacheStr);
            }

            // List of each actions to perform (in order of action) =>
            // ActionType (move/weapon/ability), movement units or weapon index, ship to target

            int actionListCnt = 1;
            // var actionListClone = new List<Tuple<ActionType, int, int>>(CurrentAP){
            //     new Tuple<ActionType, int, int>(ActionType.Move, i, 0)
            // };
            var actionListClone = new List <Tuple <ActionType, ActionParams> >(CurrentAP)
            {
                new Tuple <ActionType, ActionParams>(ActionType.Move, new MoveParams(i))
            };
            // iterate through each weapon to evaluate the possible damage
            for (int j = 0; j < weaponCnt; j++)
            {
                // calculate the value of chosing this weapon 1st (recursive function, will go until ap runs out)
                // returns maximum outcome and action list to achieve this
                float actionsValue = WeaponValue(ap, j, new int[weaponCnt], targetParams, targetDamage, actionListClone);
                if (actionsValue > maxActionValue)
                {
                    maxActionValue = actionsValue;
                    actionList     = new List <Tuple <ActionType, ActionParams> >(actionListClone);
                }

                // str = new System.Text.StringBuilder($"Action List: {actionsValue} => {actionListClone.Count}\n");
                // foreach (var action in actionListClone)
                // {
                //     str.Append($"Action: {action.Item1}, ");
                //     switch (action.Item1) {
                //         case ActionType.Move:
                //             str.Append($"Units: {action.Item2}\n");
                //             break;
                //         case ActionType.Weapon:
                //             str.Append($"Weapon Index: {action.Item2}, Target: {action.Item3}\n");
                //             break;
                //     }
                // }
                // Debug.Log(str);
                actionListClone.RemoveRange(actionListCnt, actionListClone.Count - 1);
            }
            // "moves" the ship in direction of vector field
            for (int j = 0; j < properties.Speed.Value; j++)
            {
                position += field.GetDirection(position);
            }
            // Debug.Log($"position: {position}");
            // recalculates the planes of attack (assumes no rotation currently)
        }
        // if max action isn't that great just do full movement (can be changed to save ap)
        if (maxActionValue < minActionValue)
        {
            actionList = new List <Tuple <ActionType, ActionParams> > {
                // new Tuple<ActionType, ActionParams>(ActionType.Move, new MoveParams(CurrentAP))
            }
        }
        ;

        str = new System.Text.StringBuilder($"Action List: {maxActionValue} => {actionList.Count}\n");
        foreach (var action in actionList)
        {
            str.Append($"Action: {action.Item1}, {action.Item2}\n");
            // switch (action.Item1) {
            //     case ActionType.Move:
            //         str.Append(action.Item2 + "\n");
            //         break;
            //     case ActionType.Weapon:
            //         str.Append($"Weapon Index: {action.Item2}, Target: {action.Item3}\n");
            //         break;
            // }
        }
        Debug.Log(str);

        return(actionList);
    }

    float WeaponValue(int ap, int weaponIndex,
                      int[] weaponCooldowns,
                      Tuple <float, FiringZone.Face>[] targetParams,
                      WeaponDamageRange[] targetDamage,
                      List <Tuple <ActionType, ActionParams> > actionList)
    {
        // can't afford to use this weapon
        if (ap < Weapons[weaponIndex].apCost ||
            Weapons[weaponIndex].CurrentCooldown > 0 ||
            weaponCooldowns[weaponIndex] > 0)
        {
            return(0);
        }

        int          shipCnt    = PlayerShip.PlayerShips.Count;
        float        maxDmgSum  = 0;
        WeaponParams parameters = null;

        // Find the ship that would take the most damage from this weapon
        for (int i = 0; i < shipCnt; i++)
        {
            Ship target = PlayerShip.PlayerShips[i];
            int  index  = weaponIndex * shipCnt + i;
            if (targetParams[index] == null ||
                //     targetParams[index].Item1 < minAccuracy ||
                targetParams[index].Item2 == FiringZone.Face.None ||
                false)
            {
                continue;
            }

            // Where the damage is calculated
            // Vector2 dmg = CalculateDamageRange(targetDamage);
            // dmg *= targetParams[index].Item1;
            // float dmgSum = dmg.x + dmg.y;
            WeaponDamageRange dmgRange = CalculateDamageRange(
                Weapons[weaponIndex],
                targetParams[index].Item1,
                targetParams[index].Item2,
                target.properties.Shield.Value,
                targetDamage[i].shieldRange.x
                );
            float dmgSum = dmgRange.DamageSum();
            if (dmgSum > maxDmgSum)
            {
                maxDmgSum  = dmgSum;
                parameters = new WeaponParams(weaponIndex, i, targetParams[index].Item1, dmgRange);
            }
        }
        Debug.Log($"Damage Sum: {maxDmgSum} {parameters}");
        if (maxDmgSum <= 0 || parameters == null)
        {
            return(0);
        }
        targetDamage[parameters.target] += parameters.range;
        actionList.Add(new Tuple <ActionType, ActionParams>(ActionType.Weapon, parameters));
        ap -= Weapons[weaponIndex].apCost;
        weaponCooldowns[weaponIndex] = Weapons[weaponIndex].TurnCooldown;

        return(maxDmgSum);

        int   weaponCnt       = Weapons.Length;
        float maxWeaponValue  = 0;
        int   actionListCnt   = actionList.Count;
        var   actionListClone = new List <Tuple <ActionType, ActionParams> >(actionList);

        // Find what next set of actions would get the most benefit
        for (int i = 0; i < weaponCnt; i++)
        {
            // recursion
            float weaponDmg = WeaponValue(ap, i, weaponCooldowns, targetParams, targetDamage, actionListClone);
            if (weaponDmg > maxWeaponValue)
            {
                maxWeaponValue = weaponDmg;
                actionList     = new List <Tuple <ActionType, ActionParams> >(actionListClone);
            }
            actionListClone.RemoveRange(actionListCnt, actionListClone.Count - actionListCnt);
        }
        return(maxDmgSum + maxWeaponValue);
    }
}