public override OrderedDictionary Vector() { OrderedDictionary v = base.Vector(); //if (Auras.Count > 0) for (int i = 0; i < Auras.Count; ++i) { v.AddRange(Auras[i].Vector(), Prefix()); } //else // v.AddRange(Aura.NullVector, Prefix); v.Add($"{Prefix()}DamageTakenThisTurn", DamageTakenThisTurn); v.Add($"{Prefix()}EquippedWeapon", EquippedWeapon != 0 ? Controller.Game.IdEntityDic[EquippedWeapon].Card.AssetId : 0); v.Add($"{Prefix()}ExtraAttacksThisTurn", ExtraAttacksThisTurn); v.Add($"{Prefix()}Fatigue", Fatigue); v.AddRange(HeroPower.Vector(), Prefix()); v.Add($"{Prefix()}HeroPowerDamage", HeroPowerDamage); if (Weapon != null) { v.AddRange(Weapon.Vector(), Prefix()); } //v.AddRange(Weapon != null ? Weapon.Vector : Weapon.NullVector, Prefix); return(v); }
private HeroPower GetHeroPower(SabberStoneCore.Model.Entities.HeroPower heroPower) { return(new HeroPower() { CardId = heroPower.Card.AssetId, Cost = heroPower.Cost, Exhausted = heroPower.IsExhausted }); }
/// <summary>Builds a new subclass of entity that can be added to a SabberStone game instance.</summary> /// <param name="controller">The controller of the entity.</param> /// <param name="card">The card from which the entity must be derived.</param> /// <param name="tags">The tags preset for the entity.</param> /// <param name="zone">The zone in which the entity must spawn.</param> /// <param name="id">The EntityID to assign to the newly created entity.</param> /// <returns></returns> /// <exception cref="EntityException"></exception> public static IPlayable FromCard(Controller controller, Card card, Dictionary <GameTag, int> tags = null, IZone zone = null, int id = -1) { tags = tags ?? new Dictionary <GameTag, int>(); tags[GameTag.ENTITY_ID] = id > 0 ? id : controller.Game.NextId; tags[GameTag.CONTROLLER] = controller.PlayerId; tags[GameTag.ZONE] = zone != null ? (int)zone.Type : 0; //tags[GameTag.CARD_ID] = card.AssetId; IPlayable result = null; switch (card.Type) { case CardType.MINION: result = new Minion(controller, card, tags); break; case CardType.SPELL: result = new Spell(controller, card, tags); break; case CardType.WEAPON: result = new Weapon(controller, card, tags); break; case CardType.HERO: // removing this because it's always the cards health or it is given by previous heros like for deathknight //tags[GameTag.HEALTH] = card[GameTag.HEALTH]; tags[GameTag.ZONE] = (int)Enums.Zone.PLAY; //tags[GameTag.FACTION] = card[GameTag.FACTION]; tags[GameTag.CARDTYPE] = card[GameTag.CARDTYPE]; //tags[GameTag.RARITY] = card[GameTag.RARITY]; //tags[GameTag.HERO_POWER] = card[GameTag.HERO_POWER]; result = new Hero(controller, card, tags); break; case CardType.HERO_POWER: tags[GameTag.COST] = card[GameTag.COST]; tags[GameTag.ZONE] = (int)Enums.Zone.PLAY; //tags[GameTag.FACTION] = card[GameTag.FACTION]; tags[GameTag.CARDTYPE] = card[GameTag.CARDTYPE]; //tags[GameTag.RARITY] = card[GameTag.RARITY]; //tags[GameTag.TAG_LAST_KNOWN_COST_IN_HAND] = card[GameTag.COST]; result = new HeroPower(controller, card, tags); break; default: throw new EntityException($"Couldn't create entity, because of an unknown cardType {card.Type}."); } // add entity to the game dic controller.Game.IdEntityDic.Add(result.Id, result); // add power history full entity if (controller.Game.History) { controller.Game.PowerHistory.Add(PowerHistoryBuilder.FullEntity(result)); } // add entity to the appropriate zone if it was given zone?.Add(result); if (result.ChooseOne) { result.ChooseOnePlayables[0] = id < 0 ? FromCard(controller, Cards.FromId(result.Card.Id + "a"), new Dictionary <GameTag, int> { [GameTag.CREATOR] = result.Id, [GameTag.PARENT_CARD] = result.Id }, controller.SetasideZone) : controller.SetasideZone.GetAll.Find(p => p[GameTag.CREATOR] == result.Id && p.Card.Id == result.Card.Id + "a"); result.ChooseOnePlayables[1] = id < 0 ? FromCard(controller, Cards.FromId(result.Card.Id + "b"), new Dictionary <GameTag, int> { [GameTag.CREATOR] = result.Id, [GameTag.PARENT_CARD] = result.Id }, controller.SetasideZone) : controller.SetasideZone.GetAll.Find(p => p[GameTag.CREATOR] == result.Id && p.Card.Id == result.Card.Id + "b"); } return(result); }
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 }
public HeroPower(SabberStoneCore.Model.Entities.HeroPower heroPower) { cardId_ = heroPower.Card.AssetId; cost_ = heroPower.Cost; exhausted_ = heroPower.IsExhausted; }
public HeroPower(SabberEntities.HeroPower playable) { CardId = playable.Card.AssetId; Cost = playable.Cost; Exhausted = playable.IsExhausted; }