Class GameLogic
This class manages the full gameplay experience, including:
- Tracking all players and their trainers.
- Handling Pokémon selection and evolution mechanics.
- Managing turn-based attacks, health, poison, and invincibility status.
- Implementing revival mechanics and faint counters for trainers.
- Displaying ASCII art, game instructions, and round/turn information.
- Maintaining timers for in-game delays and animations.
- Determining game outcomes, including wins and draws.
The game supports 2–5 players in a Free-For-All (FFA) mode. Each trainer takes turns selecting
a player to attack, and attacks are resolved according to Pokémon type, attack selection, and
special mechanics like invincibility or poisoning. The class also integrates with PokemonFactory to instantiate Pokémon dynamically.
-
Field Summary
FieldsModifier and TypeFieldDescriptionprivate ThreadThread used to implement a 5-second delay timer during gameplay.List of all trainers participating in the game.private intTotal number of players participating in the current game session.private final PokemonFactoryFactory used to create newPokemoninstances dynamically.private ThreadThread used to implement a 3-second delay timer during gameplay. -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionprivate intPrompts the user to enter the number of players for the Free-For-All (FFA) game.private booleanPrompts the user to confirm whether they want to play the Free-For-All (FFA) Pokémon game.private booleancheckIfPokemonIsFirstEvolution(Trainer currentTrainer) Checks whether the current Pokémon is in its first evolution stage.private booleancheckIfPokemonIsSecondEvolution(Trainer currentTrainer) Checks whether the current Pokémon is in its second (final) evolution stage.private booleancheckPlayerInputChoiceToAttackPlayer(String playerToBeAttacked, ArrayList<String> attackablePlayerNames) Determines whether the selected target for an attack is valid.private voidcheckPokemonAttacks(Trainer currentTrainer, Trainer targetTrainer) Handles a trainer selecting an attack for their Pokémon and executes it on the target trainer.private booleanDetermines if a given string contains only alphabetical letters (A-Z), ignoring case.private voidClears the console screen by printing multiple new lines.private voiddealWithPlayerPoisonStatus(Trainer currentTrainer) Applies poison effects to the current trainer's Pokémon at the start of their turn.private TrainerfindTrainerByName(String currentTrainer) Searches for and returns aTrainerobject by name from the list of all registered players.private voidDisplays the game instructions and important gameplay information to the players.private voidDisplays an introduction message to the players.private voidExecutes the main game loop for the Pokémon battle game.private voidgivePlayerTheirPokemon(int specificPlayer) Prompts a specific player to select their Pokémon and assigns it using thePokemonFactory.private voidhandleAttackResult(Trainer currentTrainer, String howPokemonDied) Handles the outcome of an attack on a trainer's Pokémon.private voidhandleAttackResult(Trainer currentTrainer, String message, String howPokemonDied) Handles the outcome of an attack on a trainer's Pokémon, including fainting and possible permanent death.private booleanhasTwoAttacks(Trainer currentTrainer) Determines whether the current trainer's Pokémon has two distinct attacks.private voidInitializes the 3-second and 5-second timer threads used for gameplay pauses.private booleanisNameUnique(ArrayList<Trainer> listOfAllPlayers, String newPlayer) Determines if a given trainer name is unique among the currently registered trainers.private voidplayerPokemonDied(Trainer currentTrainer, String message) Determines the outcome when a trainer's Pokémon faints during the game.private voidplayerWasChosenToBeAttacked(Trainer currentTrainer, Trainer targetTrainer, int numInput) Executes an attack from the current trainer's Pokémon on a target trainer's Pokémon.private voidpokemonDiedPermanently(Trainer currentTrainer, String message) Handles the permanent death of a trainer's Pokémon.private voidpokemonRevivedAfterDying(Trainer currentTrainer, String message) Handles the revival of a trainer's Pokémon after fainting.private voidCollects and registers information for all players participating in the game.private voidprintAsciiArtPokemon(String pokemon) Displays the ASCII art representation of a given Pokémon.private voidprintOutPlayersPokemonFaintStats(Trainer currentTrainer, int evolutionStage, String message) Updates a trainer's Pokémon after it faints and handles potential revival and evolution.private voidroundIntroductionForPlayerTurn(Trainer currentTrainer, int roundNumber) Displays information about the current round and the active player's turn.private voidPrints a consistent screen divider for UI formatting.private voidselectPlayerToAttack(Trainer currentTrainer) Allows the current trainer to select an opponent to attack during their turn.voidStarts the Pokémon battle game and manages the entire game flow.private StringtellsFactoryWhichPokemonToCreate(Trainer currentTrainer, int evolutionStage) Determines the correct Pokémon name to create in the factory based on the trainer's current Pokémon and the desired evolution stage.private voidupdatePlayerInvincibleStatus(Trainer currentTrainer) Updates the invincibility status of the current trainer's Pokémon at the start of their turn.
-
Field Details
-
listOfAllPlayers
-
pokemonFactory
Factory used to create newPokemoninstances dynamically.This allows assigning Pokémon to trainers and handling evolution during gameplay.
-
fiveSecondTimer
Thread used to implement a 5-second delay timer during gameplay.Commonly used to give players a short pause to view information or animations before proceeding.
-
threeSecondTimer
Thread used to implement a 3-second delay timer during gameplay.Typically used for shorter pauses, such as showing attack results or faint notifications.
-
playerAmount
private int playerAmountTotal number of players participating in the current game session.Used to control game loops, turn order, and end-of-game conditions.
-
-
Constructor Details
-
GameLogic
public GameLogic()
-
-
Method Details
-
startGame
public void startGame()Starts the Pokémon battle game and manages the entire game flow.This method performs the following actions in sequence:
- Displays the Pokémon game logo and decorative screen elements.
- Introduces the game and explains the rules and game mode.
- Prompts the user to confirm if they want to play the game.
- If the user agrees, initializes timing threads for delays and animations.
- Asks how many players will participate and collects their names.
- Allows each player to choose a starter Pokémon and displays the corresponding ASCII art.
- Displays detailed game instructions, including turn-based mechanics and evolution/respawn rules.
- Executes the main game loop, where players take turns attacking, and Pokémon faint, revive, or evolve.
The method handles player input validation, updates Pokémon and trainer status, and determines the game's winner or if the game ends in a draw.
-
gameIntroduction
private void gameIntroduction()Displays an introduction message to the players.Explains the game mode (Free-For-All), the turn-based mechanics, and what players need to do before the game begins, such as setting their names and choosing starter Pokémon.
-
populatePlayerInformation
private void populatePlayerInformation()Collects and registers information for all players participating in the game.For each player, prompts for their trainer name and validates it according to:
- Minimum and maximum length (2–20 characters)
- Alphabetical characters only
- Uniqueness among all previously entered player names
-
checkStringInputContainsOnlyLetters
Determines if a given string contains only alphabetical letters (A-Z), ignoring case.This method is used to validate player names during registration to ensure that they do not contain numbers, spaces, or special characters.
- Parameters:
input- the string to validate- Returns:
trueif the string consists solely of letters (A-Z or a-z),falseotherwise
-
isNameUnique
Determines if a given trainer name is unique among the currently registered trainers.This method is used during player registration to prevent duplicate trainer names. The comparison is case-insensitive.
- Parameters:
listOfAllPlayers- the list of trainers already registered in the gamenewPlayer- the trainer name to validate for uniqueness- Returns:
trueif no existing trainer has the same name (case-insensitive),falseif the name is already taken
-
givePlayerTheirPokemon
private void givePlayerTheirPokemon(int specificPlayer) Prompts a specific player to select their Pokémon and assigns it using thePokemonFactory.The player is presented with a choice of starter Pokémon (Squirtle, Charmander, Bulbasaur, or Pikachu). Input is validated to ensure a valid selection. Once a valid choice is made:
- The chosen Pokémon is assigned to the player's
Trainer.currentPokemon. - The ASCII art of the selected Pokémon is displayed.
- A 5-second timer delay is executed before clearing the screen.
- Parameters:
specificPlayer- the 1-based index of the player currently selecting a Pokémon- Throws:
RuntimeException- if the 5-second timer thread is interrupted unexpectedly
- The chosen Pokémon is assigned to the player's
-
printAsciiArtPokemon
Displays the ASCII art representation of a given Pokémon.The method converts the Pokémon name to uppercase and matches it against known Pokémon names. If a match is found, the corresponding ASCII art from
PokemonArtis printed to the console.If the provided Pokémon name is not recognized, a message is printed indicating that ASCII art for the Pokémon is not yet available.
- Parameters:
pokemon- the name of the Pokémon whose ASCII art should be displayed
-
gameInstructions
private void gameInstructions()Displays the game instructions and important gameplay information to the players.This includes rules about:
- How to select a player to attack during a turn
- The revival mechanics of Pokémon after fainting, including evolution chances
- The order of play based on player registration
-
gameLogic
private void gameLogic()Executes the main game loop for the Pokémon battle game.This method handles all rounds and turns, including:
- Checking which trainers are alive and eligible to take a turn
- Updating Pokémon status effects such as invincibility and poison
- Displaying round information and prompting players to select opponents
- Resolving attacks, including damage, fainting, revival, and evolution
- Determining when the game ends (either a single winner or a draw)
The method repeatedly loops through all registered trainers until only one trainer remains alive, reversing the order of play at the start to give the last-registered player the first turn. Status updates and round information are displayed between turns, and ASCII art is printed for Pokémon as needed.
Exceptions related to Pokémon permanently dying during a turn are caught internally to allow the loop to continue without crashing.
-
askUserIfTheyWantToPlay
private boolean askUserIfTheyWantToPlay()Prompts the user to confirm whether they want to play the Free-For-All (FFA) Pokémon game.Continuously asks the user until a valid response is entered:
- "Yes" (case-insensitive) – the game will start
- "No" (case-insensitive) – the game will not start
- Returns:
trueif the user declines to play (responds "No");falseif the user agrees to play (responds "Yes")
-
askHowManyPlayersArePlaying
private int askHowManyPlayersArePlaying()Prompts the user to enter the number of players for the Free-For-All (FFA) game.Continuously requests input until the user enters a valid number between 2 and 5 (inclusive), representing the total players including themselves. Invalid input will trigger a reprompt with guidance.
- Returns:
- the total number of players participating in the game (2-5)
-
initThreads
private void initThreads()Initializes the 3-second and 5-second timer threads used for gameplay pauses.The 5-second timer is used when displaying a newly chosen Pokémon to the player, giving them time to view it. The 3-second timer is used for short delays, such as between turns or after an attack. Each timer runs on a separate
Thread.Both timers print a countdown to the console, pausing for 1.5 seconds between each number.
-
clearScreen
private void clearScreen()Clears the console screen by printing multiple new lines.This method simulates a screen clear in the console to improve readability between different sections of the game, such as rounds, Pokémon selection, and attack results.
-
screenDetail
private void screenDetail()Prints a consistent screen divider for UI formatting.This method is used to separate sections of the game in the console, such as between rounds, player turns, or informational messages, providing a clear visual structure for the user.
-
updatePlayerInvincibleStatus
Updates the invincibility status of the current trainer's Pokémon at the start of their turn.If the Pokémon was previously invincible, this method removes the invincibility and notifies the player that their Pokémon is now vulnerable to attacks.
- Parameters:
currentTrainer- the trainer whose Pokémon status is being updated
-
dealWithPlayerPoisonStatus
private void dealWithPlayerPoisonStatus(Trainer currentTrainer) throws pokemonDiedPermanentlyDuringItsTurnException Applies poison effects to the current trainer's Pokémon at the start of their turn.If the Pokémon is poisoned, it loses health according to poison rules. If the Pokémon's health drops to zero, the faint counter for the trainer is decreased, and the Pokémon's death is handled (including potential permanent death or revival). Otherwise, a message is displayed indicating the health loss due to poison.
- Parameters:
currentTrainer- the trainer whose Pokémon is affected by poison- Throws:
pokemonDiedPermanentlyDuringItsTurnException- if the Pokémon dies permanently during this turn
-
roundIntroductionForPlayerTurn
Displays information about the current round and the active player's turn.This includes the round number, the trainer's name, and the attack and damage details of the trainer's current Pokémon. Prompts the player to choose an opponent to attack.
- Parameters:
currentTrainer- the trainer whose turn is being displayedroundNumber- the current round number in the game
-
selectPlayerToAttack
Allows the current trainer to select an opponent to attack during their turn.This method first generates a list of all alive opponents (excluding the current trainer) and displays their names in a readable format. It then prompts the player to type the name of the trainer they wish to attack. Input is validated to ensure it matches an available target. Once a valid opponent is selected, the method proceeds to handle the attack logic for the current trainer's Pokémon.
- Parameters:
currentTrainer- the trainer whose turn it is to attack
-
findTrainerByName
Searches for and returns aTrainerobject by name from the list of all registered players.The search is case-insensitive. If no trainer with the specified name exists in
listOfAllPlayers, this method throws aNoSuchElementException.- Parameters:
currentTrainer- the name of the trainer to find- Returns:
- the
Trainerinstance whose name matchescurrentTrainer - Throws:
NoSuchElementException- if no trainer with the given name exists inlistOfAllPlayers
-
checkPlayerInputChoiceToAttackPlayer
private boolean checkPlayerInputChoiceToAttackPlayer(String playerToBeAttacked, ArrayList<String> attackablePlayerNames) Determines whether the selected target for an attack is valid.The check is case-insensitive. The method compares the provided player name against a list of currently attackable player names to ensure the selection is allowed.
- Parameters:
playerToBeAttacked- the name of the player the current trainer wants to attackattackablePlayerNames- the list of valid player names that can be attacked- Returns:
trueifplayerToBeAttackedis inattackablePlayerNames;falseotherwise
-
checkPokemonAttacks
Handles a trainer selecting an attack for their Pokémon and executes it on the target trainer.If the current trainer's Pokémon has two available attacks, the user is prompted to choose between attack 1 or attack 2. The method validates the input to ensure only "1" or "2" is accepted. If the Pokémon has only one attack, it is automatically used.
- Parameters:
currentTrainer- the trainer whose Pokémon is performing the attacktargetTrainer- the trainer whose Pokémon is being targeted by the attack
-
hasTwoAttacks
Determines whether the current trainer's Pokémon has two distinct attacks.This method checks the Pokémon's name against the
TwoAttackPokemonenum, which contains all Pokémon that have two possible attacks. If the Pokémon is found in this enum, it is considered to have two attacks; otherwise, it has only one.- Parameters:
currentTrainer- the trainer whose Pokémon is being checked- Returns:
trueif the Pokémon has two attacks;falseotherwise
-
playerWasChosenToBeAttacked
private void playerWasChosenToBeAttacked(Trainer currentTrainer, Trainer targetTrainer, int numInput) Executes an attack from the current trainer's Pokémon on a target trainer's Pokémon.This method handles all aspects of a Pokémon attack:
- Checks if the target Pokémon is invincible. If so, the attack does nothing and a 3-second delay is applied for game pacing.
- If the target is not invincible, the current Pokémon attacks using the specified attack
index. Single-attack Pokémon use
numInput = 0. - After the attack, the results are handled, including updating health, fainting, and displaying ASCII art of the Pokémon.
- Applies a 3-second pause after the attack to allow players to see the results, then clears the screen and displays the consistent UI divider.
- Parameters:
currentTrainer- the trainer whose Pokémon is performing the attacktargetTrainer- the trainer whose Pokémon is being attackednumInput- the attack index to use; 0 if the Pokémon has only one attack- Throws:
RuntimeException- if the thread sleep is interrupted during the 3-second pause
-
handleAttackResult
Handles the outcome of an attack on a trainer's Pokémon.This method is a convenience overload that allows the caller to provide a description of how the Pokémon died without specifying a custom message prefix. It delegates to
handleAttackResult(Trainer, String, String)with an empty prefix.- Parameters:
currentTrainer- the trainer whose Pokémon is affected by the attackhowPokemonDied- a description of the cause of death or fainting event
-
handleAttackResult
Handles the outcome of an attack on a trainer's Pokémon, including fainting and possible permanent death.If the Pokémon's health is zero or below, this method:
- Decreases the trainer's faint counter.
- Calls
playerPokemonDied(Trainer, String)to handle revival, evolution, or permanent death. - Catches
pokemonDiedPermanentlyDuringItsTurnExceptionand wraps it in aRuntimeExceptionif something goes wrong.
- Parameters:
currentTrainer- the trainer whose Pokémon is affectedmessage- a custom message to display if the Pokémon survives the attackhowPokemonDied- a description of how the Pokémon fainted or died (used if health ≤ 0)
-
playerPokemonDied
private void playerPokemonDied(Trainer currentTrainer, String message) throws pokemonDiedPermanentlyDuringItsTurnException Determines the outcome when a trainer's Pokémon faints during the game.This method uses a random chance to decide whether the Pokémon revives or dies permanently. The probability of revival decreases as the trainer's faint counter increases:
- If the randomly generated number is less than or equal to the trainer's faint counter,
the Pokémon revives via
pokemonRevivedAfterDying(Trainer, String). - Otherwise, the Pokémon dies permanently via
pokemonDiedPermanently(Trainer, String), and the trainer is eliminated from the game.
- Parameters:
currentTrainer- the trainer whose Pokémon faintedmessage- a description of the cause of the Pokémon's fainting- Throws:
pokemonDiedPermanentlyDuringItsTurnException- if the Pokémon dies permanently during this turn
- If the randomly generated number is less than or equal to the trainer's faint counter,
the Pokémon revives via
-
pokemonDiedPermanently
private void pokemonDiedPermanently(Trainer currentTrainer, String message) throws pokemonDiedPermanentlyDuringItsTurnException Handles the permanent death of a trainer's Pokémon.This method performs the following actions:
- Decrements the total number of active players.
- Marks the trainer as no longer alive in the game.
- Prints a message describing the permanent elimination.
If the Pokémon dies permanently during its turn due to poison, the method also triggers a 3-second pause before throwing a
pokemonDiedPermanentlyDuringItsTurnExceptionto signal that the turn ended early.- Parameters:
currentTrainer- the trainer whose Pokémon has permanently faintedmessage- a descriptive message explaining the cause of permanent death- Throws:
pokemonDiedPermanentlyDuringItsTurnException- if the Pokémon died permanently during its turn due to poison
-
pokemonRevivedAfterDying
Handles the revival of a trainer's Pokémon after fainting.When a Pokémon faints, this method determines whether it revives and whether it evolves. Revival and evolution chances are random:
- Pokémon in its first evolution may evolve to the next stage with a 50% chance.
- Pokémon in its second evolution may evolve to its final stage with a ~33% chance.
- Otherwise, the Pokémon revives at its current stage without evolving.
- Parameters:
currentTrainer- the trainer whose Pokémon has fainted and may revivemessage- a descriptive message explaining the cause of fainting
-
checkIfPokemonIsFirstEvolution
Checks whether the current Pokémon is in its first evolution stage.The method compares the name of the trainer's current Pokémon against all Pokémon defined in the
firstEvolutionPokemonenum. This helps determine if the Pokémon is eligible for first-stage evolution upon revival.- Parameters:
currentTrainer- the trainer whose Pokémon is being checked- Returns:
trueif the Pokémon is in the first evolution stage;falseotherwise
-
checkIfPokemonIsSecondEvolution
Checks whether the current Pokémon is in its second (final) evolution stage.The method compares the name of the trainer's current Pokémon against all Pokémon defined in the
SecondEvolutionPokemonenum. This is used to determine if the Pokémon can evolve further upon revival or for display purposes.- Parameters:
currentTrainer- the trainer whose Pokémon is being checked- Returns:
trueif the Pokémon is in the second evolution stage;falseotherwise
-
printOutPlayersPokemonFaintStats
private void printOutPlayersPokemonFaintStats(Trainer currentTrainer, int evolutionStage, String message) Updates a trainer's Pokémon after it faints and handles potential revival and evolution.Depending on the
evolutionStage, this method uses thePokemonFactoryto generate the correct Pokémon instance. It then prints an informative message including:- The trainer's name
- The cause of fainting or revival
- The Pokémon's updated name after evolution (if any)
- The trainer's current chance for their Pokémon to revive again
- Parameters:
currentTrainer- the trainer whose Pokémon is being updatedevolutionStage- the evolution stage to assign: 0 = no evolution, 1 = first evolution, 2 = second evolutionmessage- a descriptive message explaining the faint or revival event
-
tellsFactoryWhichPokemonToCreate
Determines the correct Pokémon name to create in the factory based on the trainer's current Pokémon and the desired evolution stage.This method is used by
PokemonFactory.pokemonFactory(String)to generate a new Pokémon instance that corresponds to either the current stage, the first evolution, or the second evolution stage. If no valid Pokémon exists for the given evolution stage, anIllegalArgumentExceptionis thrown.- Parameters:
currentTrainer- the trainer whose Pokémon is being evolved or revivedevolutionStage- the evolution stage to create:- 0 = default (current Pokémon)
- 1 = first evolution
- 2 = second (final) evolution
- Returns:
- the name of the Pokémon corresponding to the desired stage, as a
String - Throws:
IllegalArgumentException- if no valid Pokémon exists for the given evolution stage
-