Beispiel #1
0
        public T Remove(IEntity Entity, bool ClearZone = true)
        {
            if (Entity.Zone.Type != Zone.INVALID)
            {
#if _ZONE_DEBUG
                DebugLog.WriteLine("Game " + Game.GameId + ": Removing " + Entity.ShortDescription + " from " + Entity.Controller.ShortDescription + "'s " + Type + " zone at position " + Entity.ZonePosition);
#endif
                bool removed = (Entity is T ? asList.Remove((T)Entity) : true);
                if (removed)
                {
                    if (Entity is T)
                    {
                        updateZonePositions();
                    }
                    if (ClearZone)
                    {
                        Entity[GameTag.ZONE_POSITION] = 0;
                        Entity[GameTag.ZONE]          = (int)Zone.INVALID;
                    }
                }
                else
                {
                    return(default(T));
                }
            }
            return(Entity is T ? (T)Entity : default(T));
        }
Beispiel #2
0
        public void EnqueueDeferred(IEntity source, QueueAction a)
        {
            if (a == null)
            {
                return;
            }
#if _QUEUE_DEBUG
            DebugLog.WriteLine("Queue (Game " + Game.GameId + "): Queueing action " + a + " for " + source.ShortDescription + " at depth " + Depth);
#endif
            var e = initializeAction(source, a);
            if (OnQueueing != null)
            {
                OnQueueing(this, e);
                // TODO: Count the number of arguments the cancelled action would take and remove those too
                if (!e.Cancel)
                {
                    Queue.AddBack(e);
                }
            }
            else
            {
                Queue.AddBack(e);
            }
            OnQueued?.Invoke(this, e);
        }
Beispiel #3
0
        public void Queue(TriggerType type, IEntity source)
        {
            // Check for self-death (deathrattle)
            if (type == OnDeath && source is ICharacter && ((ICharacter)source).MortallyWounded && source.Card.Behaviour.Deathrattle != null)
            {
#if _TRIGGER_DEBUG
                DebugLog.WriteLine("Game " + Game.GameId + ": Queueing deathrattle for " + source.ShortDescription);
#endif
                Game.QueueActionBlock(BlockType.TRIGGER, source, source.Card.Behaviour.Deathrattle);
                QueuedTriggersCount++;
            }

            if (!Triggers.ContainsKey(type))
            {
                return;
            }
#if _TRIGGER_DEBUG
            DebugLog.WriteLine("Game " + Game.GameId + ": Checking triggers for " + type + " initiated by " + source.ShortDescription);
#endif
            foreach (var entityId in Triggers[type])
            {
                var owningEntity = Game.Entities[entityId];
                // OnPlay triggers should be ignored for Self when entering Play zone
                if (type == OnPlay && owningEntity == source)
                {
                    continue;
                }
                // Ignore entity if not in an active zone
                Trigger trigger;
                owningEntity.Card.Behaviour.TriggersByZone[owningEntity.Zone.Type].TryGetValue(type, out trigger);
                if (trigger != null)
                {
#if _TRIGGER_DEBUG
                    DebugLog.WriteLine("Game " + Game.GameId + ": Checking trigger conditions for " + owningEntity.ShortDescription);
#endif
                    // Test trigger condition
                    if (trigger.Condition?.Eval(owningEntity, source) ?? true)
                    {
#if _TRIGGER_DEBUG
                        DebugLog.WriteLine("Game " + Game.GameId + ": Firing trigger for " + owningEntity.ShortDescription + " with actions: "
                                           + string.Join(" ", trigger.Action.Select(a => a.ToString())));
#endif
                        Game.QueueActionBlock(BlockType.TRIGGER, owningEntity, trigger.Action,
                                              // Trigger index: 0 for normal entities; -1 for Game; specific index for player if specified, otherwise -1
                                              Index: TriggerIndices.ContainsKey(type) ? TriggerIndices[type] :
                                              source == Game ? -1 : source is Player ? -1 : 0);
                        QueuedTriggersCount++;
                    }
                }
#if _TRIGGER_DEBUG
                else
                {
                    DebugLog.WriteLine("Game " + Game.GameId + ": Ignoring triggers for " + owningEntity.ShortDescription + " because it wasn't in an active zone");
                }
#endif
            }
        }
Beispiel #4
0
        public void ForceRun(IEntity owningEntity, ActionGraph g, IEntity target = null)
        {
            var actions = g.Unravel();

#if _TRIGGER_DEBUG
            DebugLog.WriteLine("Game " + Game.GameId + ": Running forced trigger for " + owningEntity.ShortDescription + " with actions: "
                               + string.Join(" ", actions.Select(a => a.ToString())));
#endif
            QueuedTriggersCount++;
            Game.RunActionBlock(BlockType.TRIGGER, owningEntity, actions, target);
        }
Beispiel #5
0
		public void StartBlock(IEntity source, List<QueueAction> qa, BlockStart gameBlock = null) {
			if (qa == null) {
				Game.OnBlockEmpty(gameBlock);
				return;
			}
#if _QUEUE_DEBUG
			DebugLog.WriteLine("Queue (Game " + Game.GameId + "): Spawning new queue at depth " + (Depth + 1) + " for " + source.ShortDescription + " with actions: " +
			                   string.Join(" ", qa.Select(a => a.ToString())) + " for action block: " + (gameBlock?.ToString() ?? "none"));
#endif
			QueueStack.Push(Queue);
			Queue = new Deque<QueueActionEventArgs>();
			BlockStack.Push(gameBlock);
			EnqueueDeferred(source, qa);
		}
Beispiel #6
0
        public void Shuffle()
        {
#if _DECK_DEBUG
            DebugLog.WriteLine("Game " + Game.GameId + ": Shuffling " + Controller.ShortDescription + "'s deck");
#endif
            var possiblePositions = Enumerable.Range(1, Count).ToList();
            foreach (var c in this)
            {
                int selIndex = RNG.Between(0, possiblePositions.Count - 1);
                // Quicker to just set zone positions than do a ton of same-zone moves
                c[GameTag.ZONE_POSITION] = possiblePositions[selIndex];
                possiblePositions.RemoveAt(selIndex);
            }
            SetDirty();
        }
Beispiel #7
0
		private void EndBlock() {
			if (Depth > 0) {
#if _QUEUE_DEBUG
				System.Diagnostics.Debug.Assert(IsBlockEmpty);
				DebugLog.WriteLine("Queue (Game " + Game.GameId + "): Destroying queue at depth " + Depth);
#endif
				Queue = QueueStack.Pop();
				var gameBlock = BlockStack.Pop();
				if (gameBlock != null)
					Game.OnBlockEmpty(gameBlock);
			}
			// When the queue is empty, notify the game - it may refill it with new actions
			if (IsEmpty)
				Game.OnQueueEmpty();
		}
Beispiel #8
0
        private void Add(IEntity entity, TriggerType type, Trigger trigger)
        {
#if _TRIGGER_DEBUG
            DebugLog.WriteLine("Game " + Game.GameId + ": Associating trigger " + type + " for entity " + entity.ShortDescription + " with game " + Game.GameId);
#endif
            if (Triggers.ContainsKey(type))
            {
                Triggers[type].Add(entity.Id);
            }
            else
            {
                Triggers = Triggers.Add(type, new List <int> {
                    entity.Id
                });
            }
        }
Beispiel #9
0
		public Task<ActionResult> ProcessBlockAsync(object UserData = null) {
#if _QUEUE_DEBUG
			DebugLog.WriteLine("Queue (Game " + Game.GameId + "): Start processing current block");
			var depth = Depth;

			// Block might not be finished if queue was cancelled
			var result = ProcessAllAsync(UserData, Depth).ContinueWith(x => {
				if (!LastActionCancelled)
					DebugLog.WriteLine("Queue (Game " + Game.GameId + "): End processing current block");
				return x.Result;
			});
#else
			var result = ProcessAllAsync(UserData, Depth);
#endif
			return result;
		}
Beispiel #10
0
        public void Add(List <Card> cards)
        {
            int nextPos = Count + 1;

            foreach (var card in cards)
            {
                var tags = new Dictionary <GameTag, int> {
                    { GameTag.ZONE, (int)Zone.DECK },
                    { GameTag.ZONE_POSITION, nextPos++ }
                };
#if _DECK_DEBUG
                DebugLog.WriteLine("Game " + Game.GameId + ": Adding " + card.Name + " to " + Controller.ShortDescription + "'s deck");
#endif
                Game.Add(Entity.FromCard(card, tags), (Player)Controller);
            }
            // Force deck zone contents to update
            SetDirty();
        }
Beispiel #11
0
        public void Fill()
        {
            // TODO: Add filter/availableCards arguments later
            var deck = Entities.Select(x => x.Card).ToList();
            var cardsAlreadyInDeck = deck.Count;
            var cardsToAdd         = StartingCards - cardsAlreadyInDeck;
            var availableCards     = Cards.Wild[HeroClass];

#if _DECK_DEBUG
            DebugLog.WriteLine("Game " + Game.GameId + ": Adding " + cardsToAdd + " random cards to " + Controller.ShortDescription + "'s deck");
#endif
            while (cardsToAdd > 0)
            {
                var card = RNG <Card> .Choose(availableCards);

                if (deck.Count(c => c == card) < card.MaxAllowedInDeck)
                {
                    deck.Add(card);
                    cardsToAdd--;
                }
            }
            Add(deck.Skip(cardsAlreadyInDeck).ToList());
        }
Beispiel #12
0
        public T Add(IEntity Entity, int ZonePosition = -1)
        {
            // Update ownership
            if (Entity.Game == null)
            {
                Game.Add(Entity, (Player)Controller);
            }

            if (Type == Zone.INVALID)
            {
                return((T)Entity);
            }

            if (ZonePosition == -1)
            {
                if (Entity is Minion)
                {
                    ZonePosition = Count + 1;
                }
                else if (Entity is Spell)
                {
                    if (Type == Zone.PLAY)
                    {
                        ZonePosition = 0;
                    }
                    else
                    {
                        ZonePosition = Count + 1;
                    }
                }
                else if (Entity is Hero || Entity is HeroPower)
                {
                    ZonePosition = 0;
                }
            }
            if (Type == Zone.SETASIDE || Type == Zone.GRAVEYARD || (Type == Zone.PLAY && Controller is Game) || Entity is Player)
            {
                ZonePosition = 0;
            }
            if (ZonePosition != 0)
            {
                if (Entity is T)
                {
                    if (IsFull)
                    {
                        throw new ZoneException("Zone size exceeded when trying to move " + Entity.ShortDescription + " to zone " + Type);
                    }
                    asList.Insert(ZonePosition - 1, (T)Entity);
                }
            }
            Entity[GameTag.ZONE] = (int)Type;

            if (Type == Zone.GRAVEYARD && Entity is Minion && Entity[GameTag.ZONE] == (int)Zone.PLAY)
            {
                Entity.Controller.NumFriendlyMinionsThatDiedThisTurn++;
                Entity.Controller.NumFriendlyMinionsThatDiedThisGame++;
            }

            if (ZonePosition == 0)
            {
                Entity[GameTag.ZONE_POSITION] = 0;
            }
            else
            {
                updateZonePositions();
            }
#if _ZONE_DEBUG
            DebugLog.WriteLine("Game " + Game.GameId + ": Adding " + Entity.ShortDescription + " to " + Entity.Controller.ShortDescription + "'s " + Type + " zone at position " + ZonePosition);
#endif
            if (Entity is T)
            {
                return((T)Entity);
            }
            return(default(T));
        }
Beispiel #13
0
        public async Task <ActionResult> ProcessAllAsync(object UserData = null, int MaxUnwindDepth = 0, bool one = false)
        {
            LastActionCancelled = false;
            bool DoneOne = false;

            while (!Paused && !IsBlockEmpty && !LastActionCancelled && !DoneOne)
            {
                if (one)
                {
                    DoneOne = true;
                }
                // Get next action and make sure it's up to date if cloned from another game
                var action = Queue.RemoveFront();
                action.Game = Game;
                if (action.Source.Game.GameId != Game.GameId)
                {
                    action.Source = Game.Entities[action.Source.Id];
                }
#if _QUEUE_DEBUG
                DebugLog.WriteLine("Queue (Game " + Game.GameId + "): Dequeued action " + action + " for " + action.Source.ShortDescription + " at depth " + Depth);
#endif
                // Get needed arguments for action from stack
                action.Args = new ActionResult[action.Action.Args.Count];
                for (int i = action.Action.Args.Count - 1; i >= 0; i--)
                {
                    // Prefer PreCompiledQueueAction items as arguments
                    var arg = action.Action.CompiledArgs[i];
                    if (!arg.HasResult)
                    {
                        // Otherwise prefer EagerQueueAction items as arguments
                        if (action.Action.EagerArgs[i] != null)
                        {
                            arg = action.Action.EagerArgs[i].Run(Game, action.Source, null);
                        }
                        else
                        {
                            // Otherwise use the ResultStack to get regular QueueAction items as arguments
                            // In this round, only pop arguments for which ActionGraph parameters were supplied
                            // to the QueueAction as these will be at the top of the stack
                            if (action.Action.Args[i] != null)
                            {
                                arg = StackPop();
                                List <IEntity> eList = arg;
                                if (eList != null && eList.Count > 0 && eList[0].Game != Game)
                                {
                                    arg = new List <IEntity>(eList.Select(e => Game.Entities[e.Id]));
                                }
                            }
                        }
                    }
                    action.Args[i] = arg;
                }
                // Now go through all of the arguments that weren't supplied as ActionGraphs and pop them off the stack
                for (int i = action.Action.Args.Count - 1; i >= 0; i--)
                {
                    if (action.Action.Args[i] == null)
                    {
                        var            arg   = StackPop();
                        List <IEntity> eList = arg;
                        if (eList != null && eList.Count > 0 && eList[0].Game != Game)
                        {
                            arg = new List <IEntity>(eList.Select(e => Game.Entities[e.Id]));
                        }
                        action.Args[i] = arg;
                    }
                }

                // Replace current UserData with new UserData if supplied
                if (UserData != null)
                {
                    this.UserData = UserData;
                }
                action.UserData = this.UserData;

                if (OnActionStarting != null)
                {
                    OnActionStarting(this, action);
                    if (action.Cancel)
                    {
                        LastActionCancelled = true;
                        continue;
                    }
                }
                var actionType = action.Action.GetType();
                if (ReplacedActions.ContainsKey(actionType))
                {
                    await ReplacedActions[actionType](this, action);
                    // action.Cancel implied when action is replaced
                    LastActionCancelled = true;
                    continue;
                }
                if (HasHistory)
                {
                    AddItem(action);
                }

                // Run action and push results onto stack
#if _QUEUE_DEBUG
                DebugLog.WriteLine("Queue (Game " + Game.GameId + "): Running action " + action + " for " + action.Source.ShortDescription + " at depth " + Depth);
#endif
                var @async = action.Action as QueueActionAsync;
                var result = (@async != null
                                        ? await @async.RunAsync(action.Game, action.Source, action.Args)
                                        : action.Action.Run(action.Game, action.Source, action.Args));
                if (result.HasResult)
                {
                    StackPush(result);
                }
                // The >= allows the current block to unwind for ProcessBlock()
                while (IsBlockEmpty && Depth >= MaxUnwindDepth)
                {
                    EndBlock();
                    if (IsEmpty)
                    {
                        break;
                    }
                }
#if _QUEUE_DEBUG
                DebugLog.WriteLine("Queue (Game " + Game.GameId + "): Finished action " + action + " for " + action.Source.ShortDescription + " at depth " + Depth);
#endif
                OnAction?.Invoke(this, action);

                // Propagate cancellation up the chain by only changing it if not already set
                LastActionCancelled = action.Cancel;
            }
            // Return whatever is left on the stack
            if (LastActionCancelled)
            {
                return(ActionResult.None);
            }
            var finalResult = ResultStack.Reverse().FirstOrDefault();
            if (!Paused && IsEmpty)
            {
                StackClear();
            }
            return(finalResult);
        }