private void LogMove(CardInPlay card, bool logPosition = false) { if (card.LastZone != card.CurrentZone) { if (logPosition) { Log.WriteLine(LogType.DebugVerbose, "{0},{1},{2},{3},{4},{5},{6},{7:0.000},{8:0.000},{9:0.000},{10:0.000}", card.Owner.ToString()[0], card.LastNonEtherZone.ToString()[0], card.LastZone.ToString()[0], card.CurrentZone.ToString()[0], card.TheCard.Name, card.CardCode, TimeCounter, card.NormalizedCenter.X, card.NormalizedCenter.Y, card.NormalizedBoundingBox.Width, card.NormalizedBoundingBox.Height); } else { Log.WriteLine(LogType.DebugVerbose, "{0},{1},{2},{3},{4},{5},{6}", card.Owner.ToString()[0], card.LastNonEtherZone.ToString()[0], card.LastZone.ToString()[0], card.CurrentZone.ToString()[0], card.TheCard.Name, card.CardCode, TimeCounter); } } }
void DrawElement(CardInPlay card, Graphics g, Color borderColor, Rectangle screenRect, bool flip) { float y = card.NormalizedBoundingBox.Y; if (flip) { y = 1 - y - card.NormalizedBoundingBox.Height; } Rectangle r = new Rectangle( (int)(0.5f + screenRect.Width / 2 + card.NormalizedBoundingBox.X * screenRect.Height), (int)(0.5f + y * screenRect.Height), (int)(0.5f + card.NormalizedBoundingBox.Width * screenRect.Height), (int)(0.5f + card.NormalizedBoundingBox.Height * screenRect.Height)); r.Offset(screenRect.X, screenRect.Y); if (FullArtView) { card.TheCard.LoadCardArt(); if (card.CurrentZone == PlayZone.Zoom || card.CurrentZone == PlayZone.Stage || card.CurrentZone == PlayZone.Hand || card.CurrentZone == PlayZone.Field) { g.DrawImage(card.TheCard.CardArt, r); } else if (card.CurrentZone == PlayZone.Cast || card.CurrentZone == PlayZone.Battle || card.CurrentZone == PlayZone.Windup || card.CurrentZone == PlayZone.Attack) { card.TheCard.DrawCardBanner(g, r); } } else { r.Offset(screenRect.X, screenRect.Y); g.DrawRectangle(new Pen(borderColor, 2), r); string text = string.Format("{0}\r\n{1}\r\n{2}\r\n{3}", CardLibrary.GetCard(card.CardCode).Name, card.CardCode, card.NormalizedCenter.Y, card.NormalizedBoundingBox.Height); TextRenderer.DrawText(g, text, this.Font, r, borderColor, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter); } }
/// <summary> /// Log moves for cards that appeared in a new zone, for a specific player /// </summary> /// <param name="last"></param> /// <param name="current"></param> /// <param name="opponentBattlingUnits">Opponent's battling units, used to find opposing unit in attack</param> /// <param name="isPlayerAttacking">If true, this player is attacking</param> private void LogMovedInCards(CardList <CardInPlay> last, CardList <CardInPlay> current, CardList <CardInPlay> opponentBattlingUnits, bool isPlayerAttacking) { bool[] logged = Enumerable.Repeat(false, current.Count).ToArray(); // First remove exact mathces. This is important when multiple of the same card are in the same zone // Order of matching in that case does matter for (int i = 0; i < current.Count; i++) { CardInPlay card = current[i]; int index = last.FindIndex(x => x.CardCode == card.CardCode && x.LastNonEtherZone == card.LastNonEtherZone && x.LastZone == card.LastZone); if (index >= 0) { last.RemoveAt(index); logged[i] = true; } } // Now look for moved cards for (int i = 0; i < current.Count; i++) { if (logged[i]) { continue; } CardInPlay card = current[i]; int index = last.FindIndex(x => x.CardCode == card.CardCode); if (index >= 0) { last.RemoveAt(index); } else if (card.CurrentZone != card.LastNonEtherZone) { LogMove(card, opponentBattlingUnits, isPlayerAttacking); } } }
/// <summary> /// Log a single moved card /// </summary> /// <param name="card">Card to log</param> /// <param name="opponentBattlingUnits">Opponent's battling units, used to find opposing unit in attack</param> /// <param name="isPlayerAttacking">If true, this player is attacking</param> private void LogMove(CardInPlay card, CardList <CardInPlay> opponentBattlingUnits, bool isPlayerAttacking) { int index; LogType logType = (card.Owner == PlayerType.LocalPlayer) ? LogType.Player : LogType.Opponent; string action = null; string target = ""; switch (card.CurrentZone) { case PlayZone.Deck: if (card.LastNonEtherZone == PlayZone.Zoom) { action = "Shuffled into Deck"; } break; case PlayZone.Tossing: if (card.LastNonEtherZone == PlayZone.Deck) { action = "Drawing from deck"; } break; case PlayZone.Zoom: if (card.LastNonEtherZone == PlayZone.Deck) { action = "Drawn from deck"; } break; case PlayZone.Stage: if (card.LastNonEtherZone == PlayZone.Deck) { action = "Presented"; } break; case PlayZone.Hand: if (card.LastNonEtherZone == PlayZone.Field || card.LastNonEtherZone == PlayZone.Battle) { action = "Recalled to Hand"; } else if (card.LastNonEtherZone != PlayZone.Zoom) { action = "Added to Hand"; } break; case PlayZone.Cast: action = "Cast"; break; case PlayZone.Field: if (card.LastNonEtherZone == PlayZone.Stage || card.LastNonEtherZone == PlayZone.Hand) { action = "Played"; } else if (card.LastNonEtherZone == PlayZone.Unknown || card.LastNonEtherZone == PlayZone.Tossing) { action = "Summoned"; } break; case PlayZone.Battle: //action = "Placed"; break; case PlayZone.Windup: if (card.LastNonEtherZone != PlayZone.Attack) { action = "Attacking"; logType = LogType.Debug; } /*index = opponentBattlingUnits.FindIndex(x => Math.Abs(card.NormalizedCenter.X - x.NormalizedCenter.X) < 0.05); * if (index == -1) * { * target = " against Face"; * } * else * { * target = string.Format(" against {0}", opponentBattlingUnits[index].TheCard.Name); * }*/ break; case PlayZone.Attack: // Look for the opposing card the opposing action = isPlayerAttacking ? "Attacked" : "Defended"; //if (card.LastNonEtherZone != PlayZone.Windup) { index = opponentBattlingUnits.FindIndex(x => Math.Abs(card.NormalizedCenter.X - x.NormalizedCenter.X) < 0.05); if (index == -1) { target = " hits Face"; } else { target = string.Format(" hits {0}", opponentBattlingUnits[index].TheCard.Name); } } //else //{ // logType = LogType.Debug; //} break; case PlayZone.Graveyard: logType = LogType.Debug; action = (card.TheCard.Type == "Spell") ? "Resolved" : "Removed"; break; case PlayZone.Ether: case PlayZone.Unknown: break; } if (action != null) { Log.WriteLine(logType, "[{0}{1}] {2}: {3}{4}", card.LastNonEtherZone.ToString()[0], card.CurrentZone.ToString()[0], action, card.TheCard.Name, target); } }
/// <summary> /// Process next rectangle layout /// </summary> /// <param name="overlay"></param> /// <param name="timestamp"></param> public void ProcessNext(Dictionary <string, JsonElement> overlay, double timestamp) { TimeCounter++; if (!TestMode && PlayerCards[(int)PlayZone.Deck].Count == 0 && PlayerCards[(int)PlayZone.Graveyard].Count == 0) { // Have not received the deck yet return; } CardList <CardInPlay> cardsInPlay = new CardList <CardInPlay>(); bool isChampionUpgrading = false; if (overlay != null) { var screen = overlay["Screen"].ToObject <Dictionary <string, JsonElement> >(); ScreenWidth = screen["ScreenWidth"].GetInt32(); ScreenHeight = screen["ScreenHeight"].GetInt32(); // We normalize elements' bounding box based on screen height. However, if screen ratio becomes // too high, screen expands height-wise. To make sure we have same behavior as before, // We adjust the height accordingly. int normalizedScreenHeight = (int)(0.5 + GameBoard.ComputeNormalizedScreenHeight(ScreenWidth, ScreenHeight)); Point correctionOffset = new Point(0, 0); var rectangles = overlay["Rectangles"].ToObject <Dictionary <string, JsonElement>[]>(); foreach (var dict in rectangles) { string cardCode = dict["CardCode"].GetString(); if (cardCode == "face") { if (dict["LocalPlayer"].GetBoolean()) { int x = dict["TopLeftX"].GetInt32(); int y = dict["TopLeftY"].GetInt32(); if (IsInitialDraw) { LocalPlayerFace = new Point(x, y); } else { correctionOffset.X = LocalPlayerFace.X - x; correctionOffset.Y = LocalPlayerFace.Y - y; } } // We don't process face continue; } Card card = CardLibrary.GetCard(cardCode); // Also ignore abilities if (card.Type != "Ability") { CardInPlay c = new CardInPlay(dict, ScreenWidth, ScreenHeight, correctionOffset, normalizedScreenHeight); cardsInPlay.Add(card.Cost, card.Name, c); if (c.CurrentZone == PlayZone.Hand && c.NormalizedCenter.Y > 1.3f) { // Champion is upgrading isChampionUpgrading = true; } } } } // Split next elements between owners. Also, disregard cards with unknown zone CardList <CardInPlay> nextPlayerCards = new CardList <CardInPlay>(); CardList <CardInPlay> nextOpponentCards = new CardList <CardInPlay>(); cardsInPlay.Split(ref nextPlayerCards, ref nextOpponentCards, x => x.Owner == PlayerType.LocalPlayer && x.CurrentZone != PlayZone.Unknown); Callback.OnElementsUpdate(nextPlayerCards, nextOpponentCards, ScreenWidth, ScreenHeight); if (isChampionUpgrading) { // Bail, champion is upgrading return; } if (nextPlayerCards.FindIndex(x => x.CurrentZone == PlayZone.Stage) >= 0) { CardsAreOnStage = true; } else if (CardsAreOnStage) { // Transition, reset ether timers for (int i = 0; i < PlayerCards[(int)PlayZone.Ether].Count; i++) { PlayerCards[(int)PlayZone.Ether][i].EtherStartTime = timestamp; } CardsAreOnStage = false; } // Mark all opponent cards as "from deck" for now // This is because we cannot reliably know which ones are not from deck yet for (int i = 0; i < nextOpponentCards.Count; i++) { nextOpponentCards[i].IsFromDeck = nextOpponentCards[i].TheCard.IsCollectible; } MoveToNext(ref PlayerCards, nextPlayerCards, timestamp, IsInitialDraw, true); if (IsInitialDraw) { // Initial draw until we add some cards to hand IsInitialDraw = (PlayerCards[(int)PlayZone.Hand].Count() == 0); } MoveToNext(ref OpponentCards, nextOpponentCards, timestamp, false, false); // Purge Ether of spells that have been cast bool thoroughCleanUp = false; int handCardIndex = PlayerCards[(int)PlayZone.Hand].FindIndex(x => x.CurrentZone == PlayZone.Hand); if (PlayerCards[(int)PlayZone.Hand].Count() > 0 && PlayerCards[(int)PlayZone.Hand][0].NormalizedBoundingBox.Height < 0.235f) { thoroughCleanUp = true; } // Clean up ether -- generally move expired cards to graveyard, but also send cast champion spells back to deck if (!CardsAreOnStage) { // Find all the expiring CardList <CardInPlay> cardsGoingToGraveyard = CleanUpEther(ref PlayerCards[(int)PlayZone.Ether], timestamp, thoroughCleanUp); for (int i = 0; i < cardsGoingToGraveyard.Count; i++) { if (cardsGoingToGraveyard[i].ChampionCode.Length > 0 && cardsGoingToGraveyard[i].TheCard.Type == "Spell") { var championCard = CardLibrary.GetCard(cardsGoingToGraveyard[i].ChampionCode); var championCardInPlay = new CardInPlay(championCard); championCardInPlay.LastZone = PlayZone.Ether; championCardInPlay.LastNonEtherZone = cardsGoingToGraveyard[i].LastNonEtherZone; championCardInPlay.CurrentZone = PlayZone.Deck; PlayerCards[(int)PlayZone.Deck].Add(championCard.Cost, championCard.Name, championCardInPlay); cardsGoingToGraveyard[i].IsFromDeck = false; Log.WriteLine(LogType.Player, "[{0}{1}] Shuffled back: {2}", championCardInPlay.LastNonEtherZone.ToString()[0], championCardInPlay.CurrentZone.ToString()[0], championCard.Name); } } PlayerCards[(int)PlayZone.Graveyard].AddRange(cardsGoingToGraveyard); } OpponentCards[(int)PlayZone.Graveyard].AddRange(CleanUpEther(ref OpponentCards[(int)PlayZone.Ether], timestamp, true)); NotifyCardSetUpdates(); // Update attacking player int numPlayerAttackers = PlayerCards[(int)PlayZone.Battle].Count + PlayerCards[(int)PlayZone.Windup].Count + PlayerCards[(int)PlayZone.Attack].Count; int numOpponentAttackers = OpponentCards[(int)PlayZone.Battle].Count + OpponentCards[(int)PlayZone.Windup].Count + OpponentCards[(int)PlayZone.Attack].Count; if (AttackingPlayer == PlayerType.None) { if (numPlayerAttackers != numOpponentAttackers) { AttackingPlayer = numPlayerAttackers > numOpponentAttackers ? PlayerType.LocalPlayer : PlayerType.Opponent; } } else if (numPlayerAttackers == 0 && numOpponentAttackers == 0) { AttackingPlayer = PlayerType.None; } // Here we try to identify opponent's cards that did not start in deck // If a card appears in hand and we can see it, it was likely generated // Also, if a card appears in the field without going through stage first // it was likely generated by spell or another unit // Mark these cards as not from deck // Unfortunately, we have no way of tracking nabbed cards, so those will show up // as from deck for (int i = 0; i < OpponentCards[(int)PlayZone.Field].Count; i++) { if (OpponentCards[(int)PlayZone.Field][i].LastNonEtherZone == PlayZone.Unknown) { OpponentCards[(int)PlayZone.Field][i].IsFromDeck = false; } } for (int i = 0; i < OpponentCards[(int)PlayZone.Hand].Count; i++) { if (OpponentCards[(int)PlayZone.Hand][i].LastNonEtherZone == PlayZone.Unknown) { OpponentCards[(int)PlayZone.Hand][i].IsFromDeck = false; } } }