Category talk

Mulligan

From Shadow Era Wiki


Mulligan

What is a Mulligan?

A mulligan allows players to return their initial hand of cards and draw a new set. This mechanism is essential for maintaining balance and fairness, ensuring that players have a chance to start with usable cards.


Code Summary and Detailed Code

FILE REFERENCES IN THE GAME
TERMS TO LOCATE MULLIGAN REFERENCE
  • AIController.cs: Manages AI player logic, including conditions for entering the `mulligan` state and invoking the `DoMulligan()` method to handle AI-specific mulligan actions.
  • PlayerController.cs: Contains player interaction logic, with specific handling for the `mulligan` state, allowing players to initiate and execute a mulligan through the `Mulligan()` method.
  • ReplayController.cs: Manages game replays, with logic to activate the `mulligan` state when processing replay commands.
  • GameController.cs: Implements the `Mulligan(ShadowEraCard card)` method to manage the process of performing a mulligan, including logging and transitioning to the `mulliganDraw` state.
  • GameOptions.cs: Contains the `mulligans` boolean option, allowing players to enable or disable the mulligan feature when setting up game options.
  • GameManager.cs: Defines an enumeration for move types that includes `mulligan`, indicating that this action is tracked as part of game moves.
  • Gameplay.cs: Supervises the game logic and checks for the `mulligan` state during game updates, enabling specific actions when in this state.
  • GameState.cs: Defines game states including `mulligan` and `mulliganDraw`, providing a framework for managing the transitions related to the mulligan mechanism.
  • Lobby.cs: Manages game lobby interactions, with options related to the `mulligans` toggle when creating or joining games.
  • Mulligan
  • Hand
  • Draw
  • Discard
  • Shuffle
SUMMARY OF THE MULLIGAN PROCESS:
  1. `GameManager.mpGameOptions.mulligans`:
    Checks whether the mulligan feature is enabled in the game's settings.
  2. `GameManager.RecordMove(GameManager.MoveTypeEnum.mulligan, null);` :
    Records the action of performing a mulligan in the game history.
  3. `GameModel.GameStateActivate(GameState.GameStateType.mulligan)` :
    Activates the `mulligan` game state, allowing the game to enter the mulligan phase.
  4. `GameModel.HandCount()` :
    Used within the `Mulligan` function to determine how many cards the player currently has in hand. It helps in deciding how many cards to draw back.
  5. `GameModel.ShuffleDeck()` :
    Shuffles the deck after cards have been discarded as part of the mulligan process, ensuring the draw is random.
  6. `GameModel.GameStateActivate(GameState.GameStateType.mulliganDraw)` :
    Activates the `mulliganDraw` state, transitioning the game into the phase where new cards are drawn for the player.
  7. `GameModel.CurSide()` :
    Retrieves the current player side, ensuring the appropriate player is drawing cards during the mulligan process.
DETAILED CODE
This code is a collection of all the functions involved in the shuffle mechanic, though it doesn't exist as a single block in any one file.
// GameManager.cs
public enum MoveTypeEnum
{
    mulligan, // Represents the mulligan action
    // other move types...
}
 
// PlayerController.cs
public void Mulligan()
{
    // Check if no card is clicked and graveyard/deck lists are not showing
    if (clickedCard == null && !GraveyardListDisplay.isShowingGraveYard && !DeckListDisplay.isShowingDeck)
    {
        GameManager.PressedGUIButton(); // Log button press
        GameManager.RecordMove(GameManager.MoveTypeEnum.mulligan, null); // Record the mulligan move
        GameController.Mulligan(null); // Call the Mulligan function in GameController with no specific card
        gameplay.ShowButton(gameplay.buttonBR2Red, "Mulligan"); // Show the Mulligan button
 
        // If the player confirms the mulligan by pressing the button
        if (gameplay.button2Pressed)
        {
            GameManager.PressedGUIButton(); // Log button press
            GameManager.RecordMove(GameManager.MoveTypeEnum.mulligan, GameModel.GetHero()); // Record the move with the hero
            GameController.Mulligan(GameModel.GetHero()); // Call the Mulligan function with the selected hero
        }
    }
}
 
// GameController.cs
public void Mulligan(ShadowEraCard card)
{
    if (card == null) // If no specific card is selected
    {
        DebugLogger.Log("GameController Mulligan Skipped - curSide " + GameModel.CurSide() + " turnCounter " + GameModel.turnCounter);
        GameModel.SetCurSide(1 - GameModel.CurSide()); // Change the current side
        GameModel.turnCounter++; // Increment the turn counter
    }
    else // If a specific card is selected
    {
        DebugLogger.Log("GameController Mulligan - curSide " + GameModel.CurSide() + " turnCounter " + GameModel.turnCounter);
        GameModel.DiscardCard(card); // Discard the selected card
        GameModel.ShuffleDeck(); // Shuffle the deck after discarding
        GameModel.GameStateActivate(GameState.GameStateType.mulliganDraw); // Transition to the mulligan draw state
    }
}
 
// GameModel.cs
public int HandCount()
{
    // Returns the number of cards in the player's hand
}
 
// DiscardCard function to handle removing a specified card from the player's hand
public void DiscardCard(ShadowEraCard card)
{
 
}
 
// Logic to shuffle the player's deck
public void ShuffleDeck()
{
 
}
 
// Logic to change the game state
public void GameStateActivate(GameState.GameStateType newState)
{
 
}
 
// Gameplay.cs
public void Update(float deltaTime)
{
    switch (GameModel.CurGameState()) // Check the current game state
    {
        case GameState.GameStateType.mulligan:
            // Handle actions specific to the mulligan state
            break;
        case GameState.GameStateType.mulliganDraw:
            if (stateTime == deltaTime)
            {
                for (int i = 0; i < 5; i++) // Draw 5 new cards for the player
                {
                    GameModel.Draw(GameModel.CurSide());
                }
            }                //In the mulligan case, the break statement is placed immediately after the comment,
            break;       //meaning no logic is executed when the game state is mulligan. This means the game
    }                        //won't perform any actions related to the mulligan phase when it’s active, which is likely
}                            //unintended.
                             //Typically, we would expect some logic to handle the actions specific to the mulligan
                             //state, such as displaying buttons for the player to choose whether to mulligan or skip.
// GameState.cs
public enum GameStateType
{
    pre,
    draw,
    mulligan, // Mulligan state
    mulliganDraw, // State when drawing new cards after a mulligan
    sacrifice,
    action,
    // other states...
}
 
// AIController.cs (if applicable for automated actions)
public void HandleMulligan() // Located in AIController.cs
{
    // Logic to automatically decide on a mulligan based on AI criteria
}

A Hidden & Unfinished Mulligan?

While the game's code incorporates a mulligan feature, it is not directly accessible in the game options. Players cannot see or adjust this option, suggesting that the developers may have chosen to obscure it, either for gameplay reasons or because it is not yet ready for public use.

Logical Inconsistencies
The logic behind the mulligan mechanic in Shadow Era presents several inconsistencies:
Discarding and Drawing: The code allows a player to discard a single card and then draw 5 new cards, without any specified action after the drawing of these 5 cards. This mechanism would create a starting hand that can reach 10 cards which clearly show the feature is still not yet completed.

Lack of Clarity on Discarded Cards:
The code does not specify what happens to the discarded card. Is it returned to the deck or set aside? This ambiguity regarding the management of discarded cards adds to the overall uncertainty of the mechanic.

The mulligan mechanic in Shadow Era appears to be an unfinished feature that requires adjustments to ensure balance and clarity in gameplay. Hidden from players and accompanied by questionable logic, this functionality raises concerns about its integration and effectiveness.

For both players and developers, it is essential to reconsider this option to enhance and coherently integrate it into the overall gaming experience.


Player Can Cecide Whether To Initiate A Mulligan

case GameState.GameStateType.mulligan:
	if (clickedCard == null && !GraveyardListDisplay.isShowingGraveYard && !DeckListDisplay.isShowingDeck)
	{
		gameplay.ShowButton(gameplay.buttonBR1Red, ScriptLocalization.SKIP);
		if (gameplay.button1Pressed)
		{
			GameManager.PressedGUIButton();
			GameManager.RecordMove(GameManager.MoveTypeEnum.mulligan, null);
			Mulligan(null);
		}
		gameplay.ShowButton(gameplay.buttonBR2Red, "Mulligan");
		if (gameplay.button2Pressed)
		{
			GameManager.PressedGUIButton();
			GameManager.RecordMove(GameManager.MoveTypeEnum.mulligan, GameModel.GetHero());
			Mulligan(GameModel.GetHero());

Explanation

Game State Check:
The code is within the case for the mulligan game state. This indicates that the game is currently in the phase where the player can decide whether to initiate a mulligan.

Condition Check:

`if (clickedCard == null && !GraveyardListDisplay.isShowingGraveYard && !DeckListDisplay.isShowingDeck)`:
This condition checks if:
No card has been clicked `(clickedCard == null)`.
The graveyard display is not showing.
The deck list is not showing.
If all these conditions are met, it allows the player to interact with the mulligan options.

Showing Buttons:

Skip Button:
`gameplay.ShowButton(gameplay.buttonBR1Red, ScriptLocalization.SKIP);` displays a button labeled "Skip". This button allows the player to bypass the mulligan phase.
Mulligan Button:
`gameplay.ShowButton(gameplay.buttonBR2Red, "Mulligan");` displays a button labeled "Mulligan". This button allows the player to initiate the mulligan process.

Button Press Logic:

Skip Button Pressed: If the player presses the Skip button `(if (gameplay.button1Pressed))`, the following actions occur:
The button press is logged with `GameManager.PressedGUIButton();`.
The move is recorded as a mulligan without a specific card `(GameManager.RecordMove(GameManager.MoveTypeEnum.mulligan, null);)`.
The `Mulligan(null);` function is called to handle the skip action.

Mulligan Button Pressed:
If the player presses the Mulligan button `(if (gameplay.button2Pressed))`, the following actions occur:
The button press is logged similarly.
The move is recorded as a mulligan associated with the selected hero `(GameManager.RecordMove(GameManager.MoveTypeEnum.mulligan, GameModel.GetHero());)`.
The `Mulligan(GameModel.GetHero());` function is called to execute the mulligan process with the selected hero.