/// <summary> /// Get the card layout for a <see cref="TableauPile"/>. /// </summary> /// <returns> /// An array of integers where the i-th integer denotes the vertical shift, in pixels, of the i-th card; the 0-th elements /// represents the shift for the TOP-MOST card. /// </returns> private float[] GetTableauPileCardLayout(Card[] cards) { // Choose an arbitrary card and get its texture. float cardHeight = Card.GetTexture(CardSuit.Clubs, CardRank.Ace).Height; float pixelVisibility = cardHeight * PercentPixelVisibility; // The number of cards (including the first one) that can be fitted into the area without shrinking // the pixel visibility for a group of cards. int minimumCards = (int)Math.Ceiling((fullAreaHeight - cardHeight) / pixelVisibility); // The minimum pixel visibility when trying to fit all the cards in the tableau pile. float minimumPixelVisibility = (fullAreaHeight - cardHeight) / (MaximumSize - 1); int excess = cards.Length - minimumCards; float compressionFactor = cardHeight * PercentCardCompressionFactor; // Check if one of the cards in this tableau pile IN the compressed group is selected GameplayScreen gameplayScreen = MainGame.Context.GameScreenManager.Get <GameplayScreen>(); bool isCompressedSelected = false; if (gameplayScreen.CurrentSelection != null) { for (int i = 0; i < cards.Length; i++) { if (gameplayScreen.CurrentSelection.Card != cards[i]) { continue; } isCompressedSelected = i < CardCompressionGroupSize - 1; break; } } float compressionVisibility = isCompressedSelected ? pixelVisibility : Math.Max(pixelVisibility - excess * compressionFactor, minimumPixelVisibility); // Distribute the leftover space among the non-compressed cards. float leftoverVisibility = (fullAreaHeight - cardHeight - compressionVisibility * CardCompressionGroupSize) / (Count - CardCompressionGroupSize - 1); float[] allocations = new float[cards.Length]; for (int i = 0; i < cards.Length; i++) { // The bottom card has no shift since there is no card after it. if (i == cards.Length - 1) { continue; } if (cards.Length <= minimumCards) { allocations[i] = pixelVisibility; } else { allocations[i] = cards.Length - i <= CardCompressionGroupSize ? compressionVisibility : leftoverVisibility; } } return(allocations); }
/// <summary> /// Initialize the <see cref="TableauPile"/>s. /// </summary> private void InitializeTableauPiles() { tableauPiles = new TableauPile[TableauPileCount]; float tableauPilePositionY = GetTableauPileStartPositionY(); float cardTextureWidth = Card.GetTexture(CardSuit.Clubs, CardRank.Ace).Width; // The width of all the tableau piles (including spacing). float fullWidth = cardTextureWidth * tableauPiles.Length + TableauPileHorizontalSpacing * (tableauPiles.Length - 1); // The amount of pixels to offset each tableau pile such that they are horizontally centered. float centreOffsetX = 0.5f * (MainGame.GameScreenWidth - fullWidth); for (int i = 0; i < tableauPiles.Length; i++) { float positionX = centreOffsetX + i * (cardTextureWidth + TableauPileHorizontalSpacing); // The height of the tableau card area, in pixels. float height = MainGame.GameScreenHeight - tableauPilePositionY - TableauPileBottomPadding; tableauPiles[i] = new TableauPile(new RectangleF(positionX, tableauPilePositionY, cardTextureWidth, height), height); } }
/// <summary> /// Update the positioning information of this <see cref="TableauPile"/>. /// </summary> public void UpdateRectangles() { Card[] cards = this.ToArray(); RectangleF[] newRectangles = CalculateCardRectangles(cards); for (int i = 0; i < Count; i++) { this[i].Rectangle = newRectangles[i]; } float height = fullAreaHeight; if (Count > 0) { RectangleF?lastCardRectangle = this[Count - 1].Rectangle; if (lastCardRectangle.HasValue) { // Recalculate the tableau height height = lastCardRectangle.Value.Bottom - Rectangle.Y; } else { // If the rectangle of the final card is null, something went terribly wrong! // (especially since we set the rectangles of the cards in the loop above). // Use the full area height for the calculation instead and log this occurence. Logger.LogFunctionEntry(string.Empty, "Rectangle of last card is null!", LoggerVerbosity.Warning); } } else { // Choose an arbitrary card and get its height. height = Card.GetTexture(CardSuit.Clubs, CardRank.Ace).Height; } Rectangle = new RectangleF(Rectangle.Position, new Vector2(Rectangle.Width, height)); }