Example #1
0
        public static List <Option> PythonOptions(this Controller c, int gameId)
        {
            if (c.Choice != null)
            {
                if (c.Choice.ChoiceType == ChoiceType.GENERAL)
                {
                    return(c.Choice.Choices.Select(p => new Option(gameId, p, c.Game.IdEntityDic[p].Card.Name)).ToList());
                }

                throw new NotImplementedException();
            }

            int           controllerId = c.Id;
            List <Option> allOptions   = ManagedObjects.OptionBuffers[gameId];

            allOptions.Add(new Option(gameId, EndTurn));

            #region PlayCardTasks
            int  mana            = c.RemainingMana;
            int  zonePosRange    = c.BoardZone.Count;
            bool?spellCostHealth = null;

            Character[] allTargets      = null;
            Minion[]    friendlyMinions = null;
            Minion[]    enemyMinions    = null;
            Minion[]    allMinions      = null;
            Character[] allFriendly     = null;
            Character[] allEnemies      = null;

            ReadOnlySpan <IPlayable> handSpan = c.HandZone.GetSpan();
            for (int i = 0; i < handSpan.Length; i++)
            {
                if (!handSpan[i].ChooseOne || c.ChooseBoth)
                {
                    GetPlayCardTasks(handSpan[i]);
                }
                else
                {
                    IPlayable[] playables = handSpan[i].ChooseOnePlayables;
                    for (int j = 1; j < 3; j++)
                    {
                        GetPlayCardTasks(handSpan[i], playables[j - 1], j);
                    }
                }
            }
            #endregion

            #region HeroPowerTask
            HeroPower power         = c.Hero.HeroPower;
            Card      heroPowerCard = power.Card;
            if (!power.IsExhausted && mana >= power.Cost &&
                !c.HeroPowerDisabled && !heroPowerCard.HideStat)
            {
                if (heroPowerCard.ChooseOne)
                {
                    if (c.ChooseBoth)
                    {
                        allOptions.Add(new Option(gameId, API.Option.Types.PlayerTaskType.HeroPower, source: power));
                    }
                    else
                    {
                        allOptions.Add(new Option(gameId, API.Option.Types.PlayerTaskType.HeroPower, subOption: 1, source: power));
                        allOptions.Add(new Option(gameId, API.Option.Types.PlayerTaskType.HeroPower, subOption: 2, source: power));
                    }
                }
                else
                {
                    if (heroPowerCard.IsPlayableByCardReq(c))
                    {
                        Character[] targets = GetTargets(heroPowerCard);
                        if (targets != null)
                        {
                            for (int i = 0; i < targets.Length; i++)
                            {
                                allOptions.Add(new Option(gameId, API.Option.Types.PlayerTaskType.HeroPower,
                                                          0, Option.getPosition(targets[i], controllerId),
                                                          source: power, target: targets[i]));
                            }
                        }
                        else
                        {
                            allOptions.Add(new Option(gameId, API.Option.Types.PlayerTaskType.HeroPower, source: power));
                        }
                    }
                }
            }
            #endregion

            #region MinionAttackTasks
            Minion[] attackTargets             = null;
            bool     isOpHeroValidAttackTarget = false;
            var      boardSpan = c.BoardZone.GetSpan();
            for (int j = 0; j < boardSpan.Length; j++)
            {
                Minion minion = boardSpan[j];

                if (minion.IsExhausted && (!minion.HasCharge || minion.NumAttacksThisTurn != 0))
                {
                    continue;
                }
                if (minion.IsFrozen || minion.AttackDamage == 0 || minion.CantAttack || minion.Untouchable)
                {
                    continue;
                }

                GenerateAttackTargets();

                for (int i = 0; i < attackTargets.Length; i++)
                {
                    allOptions.Add(new Option(gameId, MinionAttack, j + 1,
                                              Option.getEnemyPosition(attackTargets[i]), source: minion, target: attackTargets[i]));
                }

                if (isOpHeroValidAttackTarget && !(minion.CantAttackHeroes || minion.AttackableByRush))
                {
                    allOptions.Add(new Option(gameId, MinionAttack, j + 1, Option.OP_HERO_POSITION,
                                              source: minion, target: c.Opponent.Hero));
                }
            }
            #endregion

            #region HeroAttackTaskts
            Hero hero = c.Hero;

            if ((!hero.IsExhausted || (hero.ExtraAttacksThisTurn > 0 && hero.ExtraAttacksThisTurn >= hero.NumAttacksThisTurn)) &&
                hero.AttackDamage > 0 && !hero.IsFrozen)
            {
                GenerateAttackTargets();

                for (int i = 0; i < attackTargets.Length; i++)
                {
                    allOptions.Add(new Option(gameId, HeroAttack, 0,
                                              Option.getEnemyPosition(attackTargets[i]), source: hero, target: attackTargets[i]));
                }

                if (isOpHeroValidAttackTarget && !hero.CantAttackHeroes)
                {
                    allOptions.Add(new Option(gameId, HeroAttack, 0, Option.OP_HERO_POSITION,
                                              source: hero, target: c.Opponent.Hero));
                }
            }
            #endregion

            return(allOptions);

            #region local functions
            void GetPlayCardTasks(in IPlayable playable, in IPlayable chooseOnePlayable = null, int subOption = -1)
            {
                Card card = chooseOnePlayable?.Card ?? playable.Card;

                if (!spellCostHealth.HasValue)
                {
                    spellCostHealth = c.ControllerAuraEffects[GameTag.SPELLS_COST_HEALTH] == 1;
                }

                bool healthCost = (playable.AuraEffects?.CardCostHealth ?? false) ||
                                  (spellCostHealth.Value && playable.Card.Type == CardType.SPELL);

                if (!healthCost && (playable.Cost > mana || playable.Card.HideStat))
                {
                    return;
                }

                // check PlayableByPlayer
                switch (playable.Card.Type)
                {
                //	REQ_MINION_CAP
                case CardType.MINION when c.BoardZone.IsFull:
                    return;

                case CardType.SPELL:
                {
                    if (card.IsSecret)
                    {
                        if (c.SecretZone.IsFull)                                 // REQ_SECRET_CAP
                        {
                            return;
                        }
                        if (c.SecretZone.Any(p => p.Card.AssetId == card.AssetId))                                 // REQ_UNIQUE_SECRET
                        {
                            return;
                        }
                    }

                    if (card.IsQuest && c.SecretZone.Quest != null)
                    {
                        return;
                    }
                    break;
                }
                }

                {
                    if (!card.IsPlayableByCardReq(c))
                    {
                        return;
                    }

                    Character[] targets = GetTargets(card);

                    int sourcePosition = playable.ZonePosition;

                    // Card doesn't require any targets
                    if (targets == null)
                    {
                        if (playable is Minion)
                        {
                            for (int i = 0; i <= zonePosRange; i++)
                            {
                                allOptions.Add(new Option(gameId, PlayCard, sourcePosition, i + 1, subOption,
                                                          source: playable));
                            }
                        }
                        else
                        {
                            allOptions.Add(new Option(gameId, PlayCard, sourcePosition, -1, subOption,
                                                      source: playable));
                        }
                    }
                    else
                    {
                        if (targets.Length == 0)
                        {
                            if (card.MustHaveTargetToPlay)
                            {
                                return;
                            }

                            if (playable is Minion)
                            {
                                for (int i = 0; i <= zonePosRange; i++)
                                {
                                    allOptions.Add(new Option(gameId, PlayCard, sourcePosition, i + 1, subOption,
                                                              source: playable));
                                }
                            }
                            else
                            {
                                allOptions.Add(new Option(gameId, PlayCard, sourcePosition, -1, subOption,
                                                          source: playable));
                            }
                        }
                        else
                        {
                            for (int j = 0; j < targets.Length; j++)
                            {
                                ICharacter target = targets[j];
                                if (playable is Minion)
                                {
                                    //for (int i = 0; i <= zonePosRange; i++)
                                    //    allOptions.Add(PlayCardTask.Any(c, playable, target, i, subOption,
                                    //        true));
                                    continue;
                                }
                                else
                                {
                                    allOptions.Add(new Option(gameId, PlayCard, sourcePosition,
                                                              Option.getPosition(target, controllerId), subOption,
                                                              source: playable, target: target));
                                }
                            }
                        }
                    }
                }
            }

            // Returns null if targeting is not required
            // Returns 0 Array if there is no available target
            Character[] GetTargets(Card card)
            {
                // Check it needs additional validation
                if (!card.TargetingAvailabilityPredicate?.Invoke(c, card) ?? false)
                {
                    return(null);
                }

                Character[] targets;

                switch (card.TargetingType)
                {
                case TargetingType.None:
                    return(null);

                case TargetingType.All:
                    if (allTargets == null)
                    {
                        if (c.Opponent.Hero.HasStealth)
                        {
                            allTargets    = new Character[GetFriendlyMinions().Length + GetEnemyMinions().Length + 1];
                            allTargets[0] = c.Hero;
                            Array.Copy(GetAllMinions(), 0, allTargets, 1, allMinions.Length);
                        }
                        else
                        {
                            allTargets    = new Character[GetFriendlyMinions().Length + GetEnemyMinions().Length + 2];
                            allTargets[0] = c.Hero;
                            allTargets[1] = c.Opponent.Hero;
                            Array.Copy(GetAllMinions(), 0, allTargets, 2, allMinions.Length);
                        }
                    }
                    targets = allTargets;
                    break;

                case TargetingType.FriendlyCharacters:
                    if (allFriendly == null)
                    {
                        allFriendly    = new Character[GetFriendlyMinions().Length + 1];
                        allFriendly[0] = c.Hero;
                        Array.Copy(friendlyMinions, 0, allFriendly, 1, friendlyMinions.Length);
                    }
                    targets = allFriendly;
                    break;

                case TargetingType.EnemyCharacters:
                    if (allEnemies == null)
                    {
                        if (!c.Opponent.Hero.HasStealth)
                        {
                            allEnemies    = new Character[GetEnemyMinions().Length + 1];
                            allEnemies[0] = c.Opponent.Hero;
                            Array.Copy(enemyMinions, 0, allEnemies, 1, enemyMinions.Length);
                        }
                        else
                        {
                            allEnemies = GetEnemyMinions();
                        }
                    }
                    targets = allEnemies;
                    break;

                case TargetingType.AllMinions:
                    targets = GetAllMinions();
                    break;

                case TargetingType.FriendlyMinions:
                    targets = GetFriendlyMinions();
                    break;

                case TargetingType.EnemyMinions:
                    targets = GetEnemyMinions();
                    break;

                case TargetingType.Heroes:
                    targets = !c.Opponent.Hero.HasStealth
                                                        ? new[] { c.Hero, c.Opponent.Hero }
                                                        : new[] { c.Hero };
                    break;

                default:
                    throw new ArgumentOutOfRangeException();
                }

                // Filtering for target_if_available
                TargetingPredicate p = card.TargetingPredicate;

                if (p != null)
                {
                    if (card.Type == CardType.SPELL || card.Type == CardType.HERO_POWER)
                    {
                        Character[] buffer = new Character[targets.Length];
                        int         i      = 0;
                        for (int j = 0; j < targets.Length; ++j)
                        {
                            if (!p(targets[j]) || targets[j].CantBeTargetedBySpells)
                            {
                                continue;
                            }
                            buffer[i] = targets[j];
                            i++;
                        }

                        if (i != targets.Length)
                        {
                            Character[] result = new Character[i];
                            Array.Copy(buffer, result, i);
                            return(result);
                        }
                        return(buffer);
                    }
                    else
                    {
                        if (!card.TargetingAvailabilityPredicate?.Invoke(c, card) ?? false)
                        {
                            return(null);
                        }

                        Character[] buffer = new Character[targets.Length];
                        int         i      = 0;
                        for (int j = 0; j < targets.Length; ++j)
                        {
                            if (!p(targets[j]))
                            {
                                continue;
                            }
                            buffer[i] = targets[j];
                            i++;
                        }

                        if (i != targets.Length)
                        {
                            Character[] result = new Character[i];
                            Array.Copy(buffer, result, i);
                            return(result);
                        }
                        return(buffer);
                    }
                }
                else if (card.Type == CardType.SPELL || card.Type == CardType.HERO_POWER)
                {
                    Character[] buffer = new Character[targets.Length];
                    int         i      = 0;
                    for (int j = 0; j < targets.Length; ++j)
                    {
                        if (targets[j].CantBeTargetedBySpells)
                        {
                            continue;
                        }
                        buffer[i] = targets[j];
                        i++;
                    }

                    if (i != targets.Length)
                    {
                        Character[] result = new Character[i];
                        Array.Copy(buffer, result, i);
                        return(result);
                    }
                    return(buffer);
                }

                return(targets);

                Minion[] GetFriendlyMinions()
                {
                    return(friendlyMinions ?? (friendlyMinions = c.BoardZone.GetAll()));
                }

                Minion[] GetAllMinions()
                {
                    if (allMinions != null)
                    {
                        return(allMinions);
                    }

                    allMinions = new Minion[GetEnemyMinions().Length + GetFriendlyMinions().Length];
                    Array.Copy(enemyMinions, allMinions, enemyMinions.Length);
                    Array.Copy(friendlyMinions, 0, allMinions, enemyMinions.Length, friendlyMinions.Length);

                    return(allMinions);
                }
            }

            void GenerateAttackTargets()
            {
                if (attackTargets != null)
                {
                    return;
                }

                Minion[] eMinions = GetEnemyMinions();
                //var taunts = new Minion[eMinions.Length];
                Minion[] taunts = null;
                int      tCount = 0;

                for (int i = 0; i < eMinions.Length; i++)
                {
                    if (eMinions[i].HasTaunt)
                    {
                        if (taunts == null)
                        {
                            taunts = new Minion[eMinions.Length];
                        }
                        taunts[tCount] = eMinions[i];
                        tCount++;
                    }
                }

                if (tCount > 0)
                {
                    var targets = new Minion[tCount];
                    Array.Copy(taunts, targets, tCount);
                    attackTargets             = targets;
                    isOpHeroValidAttackTarget = false;                      // some brawls allow taunt heros and c should be fixed
                    return;
                }
                attackTargets = eMinions;

                isOpHeroValidAttackTarget =
                    !c.Opponent.Hero.IsImmune && !c.Opponent.Hero.HasStealth;
            }

            Minion[] GetEnemyMinions()
            {
                return(enemyMinions ?? (enemyMinions = c.Opponent.BoardZone.GetAll(p => !p.HasStealth && !p.IsImmune)));
            }

            #endregion
        }
Example #2
0
 protected Targeting(Controller controller, Card card, Dictionary <GameTag, int> tags) : base(controller.Game, card, tags)
 {
     Controller = controller;
 }