mirror of
				https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-02.git
				synced 2025-10-31 08:11:52 +01:00 
			
		
		
		
	updated trade and client logic
This commit is contained in:
		| @@ -1,14 +1,5 @@ | ||||
| //////////////////////////////////////// | ||||
| // Programming project code | ||||
| // UniBw M, 2022, 2023, 2024 | ||||
| // www.unibw.de/inf2 | ||||
| // (c) Mark Minas (mark.minas@unibw.de) | ||||
| //////////////////////////////////////// | ||||
|  | ||||
| package pp.monopoly.game.client; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.lang.System.Logger; | ||||
| import java.lang.System.Logger.Level; | ||||
| import java.util.ArrayList; | ||||
| @@ -38,23 +29,25 @@ import pp.monopoly.notification.InfoTextEvent; | ||||
| import pp.monopoly.notification.Sound; | ||||
| import pp.monopoly.notification.SoundEvent; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.lang.System.Logger; | ||||
| import java.lang.System.Logger.Level; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
|  * Controls the client-side game logic for Monopoly. | ||||
|  * Manages the player's placement, interactions with the map, and response to server messages. | ||||
|  * Handles interactions with the server and game state management on the client side. | ||||
|  */ | ||||
| public class ClientGameLogic implements ServerInterpreter, GameEventBroker { | ||||
|  | ||||
|     /** Logger for the client-side game logic. */ | ||||
|     static final Logger LOGGER = System.getLogger(ClientGameLogic.class.getName()); | ||||
|  | ||||
|     /** The object responsible for sending messages to the server. */ | ||||
|     private final ClientSender clientSender; | ||||
|  | ||||
|     /** A list of listeners to receive game events. */ | ||||
|     private final List<GameEventListener> listeners = new ArrayList<>(); | ||||
|  | ||||
|     /** The game board representing the player's current state. */ | ||||
|     private Board board; | ||||
|  | ||||
|     /** The current state of the client game logic. */ | ||||
|     private ClientState state = new LobbyState(this); | ||||
|  | ||||
|     /** | ||||
| @@ -68,6 +61,8 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { | ||||
|  | ||||
|     /** | ||||
|      * Returns the current state of the game logic. | ||||
|      * | ||||
|      * @return the current state | ||||
|      */ | ||||
|     ClientState getState() { | ||||
|         return state; | ||||
| @@ -86,9 +81,9 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the player's own map. | ||||
|      * Returns the player's game board. | ||||
|      * | ||||
|      * @return the player's own map | ||||
|      * @return the player's game board | ||||
|      */ | ||||
|     public Board getBoard() { | ||||
|         return board; | ||||
| @@ -115,32 +110,23 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { | ||||
|     /** | ||||
|      * Emits an event to play the specified sound. | ||||
|      * | ||||
|      * @param sound the sound to be played. | ||||
|      * @param sound the sound to be played | ||||
|      */ | ||||
|     public void playSound(Sound sound) { | ||||
|         notifyListeners(new SoundEvent(sound)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Loads a map from the specified file. | ||||
|      * | ||||
|      * @param file the file to load the map from | ||||
|      * @throws IOException if an I/O error occurs | ||||
|      */ | ||||
|     public void loadMap(File file) throws IOException { | ||||
|         state.loadMap(file); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Sends a message to the server. | ||||
|      * | ||||
|      * @param msg the message to be sent | ||||
|      */ | ||||
|     void send(ClientMessage msg) { | ||||
|         if (clientSender == null) | ||||
|         if (clientSender == null) { | ||||
|             LOGGER.log(Level.ERROR, "trying to send {0} with sender==null", msg); //NON-NLS | ||||
|         else | ||||
|         } else { | ||||
|             clientSender.send(msg); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -172,12 +158,13 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { | ||||
|         synchronized (this) { | ||||
|             copy = new ArrayList<>(listeners); | ||||
|         } | ||||
|         for (GameEventListener listener : copy) | ||||
|         for (GameEventListener listener : copy) { | ||||
|             event.notifyListener(listener); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Called once per frame by the update loop. | ||||
|      * Updates the game logic once per frame in the update loop. | ||||
|      * | ||||
|      * @param delta time in seconds since the last update call | ||||
|      */ | ||||
| @@ -185,6 +172,11 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { | ||||
|         state.update(delta); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handles the response for buying a property. | ||||
|      * | ||||
|      * @param msg the message containing the buy property response | ||||
|      */ | ||||
|     @Override | ||||
|     public void received(BuyPropertyResponse msg) { | ||||
|         if (msg.isSuccessful()) { | ||||
| @@ -194,35 +186,65 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { | ||||
|             setInfoText("Unable to buy " + msg.getPropertyName() + ". Reason: " + msg.getReason()); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|  | ||||
|     /** | ||||
|      * Handles the result of a dice roll. | ||||
|      * | ||||
|      * @param msg the message containing the dice roll result | ||||
|      */ | ||||
|     @Override | ||||
|     public void received(DiceResult msg) { | ||||
|         setInfoText("You rolled a " + msg.calcTotal() + "!"); | ||||
|         //Set the dice images | ||||
|         playSound(Sound.DICE_ROLL); | ||||
|     } | ||||
|      | ||||
|  | ||||
|     /** | ||||
|      * Handles drawing an event card. | ||||
|      * | ||||
|      * @param msg the message containing the drawn card details | ||||
|      */ | ||||
|     @Override | ||||
|     public void received(EventDrawCard msg) { | ||||
|         setInfoText("Event card drawn: " + msg.getCardDescription()); | ||||
|         //event card logic | ||||
|         playSound(Sound.EVENT_CARD); | ||||
|     } | ||||
|      | ||||
|  | ||||
|     /** | ||||
|      * Handles the game over message. | ||||
|      * | ||||
|      * @param msg the message containing game over details | ||||
|      */ | ||||
|     @Override | ||||
|     public void received(GameOver msg) { | ||||
|         if (msg.isWinner()) { | ||||
|             setInfoText("Congratulations! You have won the game!"); | ||||
|             //Winner popup | ||||
|             playSound(Sound.WINNER); | ||||
|         } else { | ||||
|             setInfoText("Game over. Better luck next time!"); | ||||
|             // Looser popup | ||||
|             playSound(Sound.LOSER); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|  | ||||
|     /** | ||||
|      * Handles the start of the game. | ||||
|      * | ||||
|      * @param msg the game start message | ||||
|      */ | ||||
|     @Override | ||||
|     public void received(GameStart msg) { | ||||
|         setInfoText("The game has started! Good luck!"); | ||||
|         setState(new WaitForTurnState(this)); | ||||
|     } | ||||
|      | ||||
|  | ||||
|     /** | ||||
|      * Handles jail-related events. | ||||
|      * | ||||
|      * @param msg the message containing jail event details | ||||
|      */ | ||||
|     @Override | ||||
|     public void received(JailEvent msg) { | ||||
|         if (msg.isGoingToJail()) { | ||||
| @@ -232,33 +254,74 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { | ||||
|             setInfoText("You are out of jail!"); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|  | ||||
|     /** | ||||
|      * Updates the status of a player. | ||||
|      * | ||||
|      * @param msg the message containing player status update details | ||||
|      */ | ||||
|     @Override | ||||
|     public void received(PlayerStatusUpdate msg) { | ||||
|          | ||||
|         setInfoText("Player " + msg.getPlayerName() + " status updated: " + msg.getStatus()); | ||||
|     } | ||||
|      | ||||
|  | ||||
|     /** | ||||
|      * Handles timeout warnings. | ||||
|      * | ||||
|      * @param msg the message containing timeout warning details | ||||
|      */ | ||||
|     @Override | ||||
|     public void received(TimeOutWarning msg) { | ||||
|         setInfoText("Warning! Time is running out. You have " + msg.getRemainingTime() + " seconds left."); | ||||
|     } | ||||
|      | ||||
|  | ||||
|     /** | ||||
|      * Displays the player's assets in response to a server query. | ||||
|      * | ||||
|      * @param msg the message containing the player's assets | ||||
|      */ | ||||
|     @Override | ||||
|     public void received(ViewAssetsResponse msg) { | ||||
|         setInfoText("Your current assets are being displayed."); | ||||
|     } | ||||
|      | ||||
|  | ||||
|     /** | ||||
|      * Handles trade replies from other players. | ||||
|      * | ||||
|      * @param msg the message containing the trade reply | ||||
|      */ | ||||
|     @Override | ||||
|     public void received(TradeReply msg) { | ||||
|     } | ||||
|      | ||||
|     @Override | ||||
|     public void received(TradeRequest msg) { | ||||
|         if (msg.getTradeHandler().getStatus()) { | ||||
|             setInfoText("Trade accepted by " + msg.getTradeHandler().getReceiver().getName() + "."); | ||||
|             playSound(Sound.TRADE_ACCEPTED); | ||||
|         } else { | ||||
|             setInfoText("Trade rejected by " + msg.getTradeHandler().getReceiver().getName() + "."); | ||||
|             playSound(Sound.TRADE_REJECTED); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handles trade requests from other players. | ||||
|      * | ||||
|      * @param msg the message containing the trade request details | ||||
|      */ | ||||
|     @Override | ||||
|     public void received(TradeRequest msg) { | ||||
|         setInfoText("Trade offer received from " + msg.getTradeHandler().getSender().getName()); | ||||
|         // playSound(Sound.TRADE_REQUEST); no sound effect | ||||
|         // notifyListeners(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handles the transition to the next player's turn. | ||||
|      * | ||||
|      * @param msg the message indicating it's the next player's turn | ||||
|      */ | ||||
|     @Override | ||||
|     public void received(NextPlayerTurn msg) { | ||||
|         state = new ActiveState(this); | ||||
|         setInfoText("It's your turn!"); | ||||
|         setState(new ActiveState(this)); | ||||
|     } | ||||
|      | ||||
| } | ||||
|   | ||||
| @@ -6,66 +6,112 @@ import pp.monopoly.model.fields.PropertyField; | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
|  * Helper class that handles the trade logic between two players. | ||||
|  * Manages trade initiation, validation, acceptance, and rejection involving multiple properties, money, and jail cards. | ||||
|  * Handles a single trade between two players. | ||||
|  * Encapsulates trade details, validation, acceptance, and rejection. | ||||
|  */ | ||||
| public class TradeHandler { | ||||
|  | ||||
|     private final Player sender; | ||||
|     private final Player receiver; | ||||
|     private final int offeredAmount; | ||||
|     private final List<PropertyField> offeredProperties; | ||||
|     private final int offeredJailCards; | ||||
|     private final int requestedAmount; | ||||
|     private final List<PropertyField> requestedProperties; | ||||
|     private final int requestedJailCards; | ||||
|     private Boolean status = null; | ||||
|  | ||||
|     /** | ||||
|      * Initiates a trade offer between two players involving properties, money, and jail cards. | ||||
|      * Constructs a TradeHandler for a single trade instance. | ||||
|      * | ||||
|      * @param sender             the Player who is initiating the trade | ||||
|      * @param receiver           the Player who is the target of the trade offer | ||||
|      * @param offeredAmount      the amount of money the sender offers | ||||
|      * @param offeredProperties  the list of properties the sender offers | ||||
|      * @param offeredJailCards   the number of jail cards the sender offers | ||||
|      * @param requestedAmount    the amount of money the sender requests from the receiver | ||||
|      * @param requestedProperties the list of properties the sender requests from the receiver | ||||
|      * @param requestedJailCards the number of jail cards the sender requests from the receiver | ||||
|      * @return true if the trade offer is valid and initiated, false otherwise | ||||
|      * @param sender             the Player initiating the trade | ||||
|      * @param receiver           the Player receiving the trade offer | ||||
|      * @param offeredAmount      the amount of money offered by the sender | ||||
|      * @param offeredProperties  the properties offered by the sender | ||||
|      * @param offeredJailCards   the jail cards offered by the sender | ||||
|      * @param requestedAmount    the amount of money requested from the receiver | ||||
|      * @param requestedProperties the properties requested from the receiver | ||||
|      * @param requestedJailCards the jail cards requested from the receiver | ||||
|      */ | ||||
|     public boolean initiateTrade(Player sender, Player receiver, int offeredAmount, List<PropertyField> offeredProperties, | ||||
|                                  int offeredJailCards, int requestedAmount, List<PropertyField> requestedProperties, int requestedJailCards) { | ||||
|         // Validate the trade offer | ||||
|         if (!validateTrade(sender, offeredAmount, offeredProperties, offeredJailCards, receiver, requestedAmount, requestedProperties, requestedJailCards)) { | ||||
|     public TradeHandler(Player sender, Player receiver, int offeredAmount, List<PropertyField> offeredProperties, | ||||
|                         int offeredJailCards, int requestedAmount, List<PropertyField> requestedProperties, int requestedJailCards) { | ||||
|         this.sender = sender; | ||||
|         this.receiver = receiver; | ||||
|         this.offeredAmount = offeredAmount; | ||||
|         this.offeredProperties = offeredProperties; | ||||
|         this.offeredJailCards = offeredJailCards; | ||||
|         this.requestedAmount = requestedAmount; | ||||
|         this.requestedProperties = requestedProperties; | ||||
|         this.requestedJailCards = requestedJailCards; | ||||
|     } | ||||
|  | ||||
|     public int getOfferedAmount() { | ||||
|         return offeredAmount; | ||||
|     } | ||||
|  | ||||
|     public int getOfferedJailCards() { | ||||
|         return offeredJailCards; | ||||
|     } | ||||
|  | ||||
|     public List<PropertyField> getOfferedProperties() { | ||||
|         return offeredProperties; | ||||
|     } | ||||
|  | ||||
|     public Player getReceiver() { | ||||
|         return receiver; | ||||
|     } | ||||
|  | ||||
|     public int getRequestedAmount() { | ||||
|         return requestedAmount; | ||||
|     } | ||||
|  | ||||
|     public int getRequestedJailCards() { | ||||
|         return requestedJailCards; | ||||
|     } | ||||
|  | ||||
|     public List<PropertyField> getRequestedProperties() { | ||||
|         return requestedProperties; | ||||
|     } | ||||
|  | ||||
|     public Player getSender() { | ||||
|         return sender; | ||||
|     } | ||||
|  | ||||
|     public Boolean getStatus() { | ||||
|         return status; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Initiates the trade and validates its terms. | ||||
|      * | ||||
|      * @return true if the trade is valid and can proceed, false otherwise | ||||
|      */ | ||||
|     public boolean initiateTrade() { | ||||
|         if (!validateTrade()) { | ||||
|             System.out.println("Trade offer is invalid."); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // Notify the receiver about the trade offer (this would be an actual message in a real implementation) | ||||
|         System.out.println("Trade offer initiated by " + sender.getName() + " to " + receiver.getName()); | ||||
|         System.out.println("Trade initiated by " + sender.getName() + " to " + receiver.getName()); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Accepts the trade offer and completes the trade between two players. | ||||
|      * | ||||
|      * @param sender             the Player who initiated the trade | ||||
|      * @param receiver           the Player who accepted the trade | ||||
|      * @param offeredAmount      the amount of money to transfer from the sender to the receiver | ||||
|      * @param offeredProperties  the list of properties to transfer from the sender to the receiver | ||||
|      * @param offeredJailCards   the number of jail cards to transfer from the sender to the receiver | ||||
|      * @param requestedAmount    the amount of money to transfer from the receiver to the sender | ||||
|      * @param requestedProperties the list of properties to transfer from the receiver to the sender | ||||
|      * @param requestedJailCards the number of jail cards to transfer from the receiver to the sender | ||||
|      * Completes the trade by transferring money, properties, and jail cards. | ||||
|      */ | ||||
|     public void acceptTrade(Player sender, Player receiver, int offeredAmount, List<PropertyField> offeredProperties, | ||||
|                             int offeredJailCards, int requestedAmount, List<PropertyField> requestedProperties, int requestedJailCards) { | ||||
|     public void acceptTrade() { | ||||
|         // Transfer money | ||||
|         sender.earnMoney(-offeredAmount);  // Deduct money from the sender | ||||
|         receiver.earnMoney(offeredAmount); // Add money to the receiver | ||||
|         sender.earnMoney(-offeredAmount); | ||||
|         receiver.earnMoney(offeredAmount); | ||||
|  | ||||
|         receiver.earnMoney(-requestedAmount); // Deduct money from the receiver | ||||
|         sender.earnMoney(requestedAmount);    // Add money to the sender | ||||
|         receiver.earnMoney(-requestedAmount); | ||||
|         sender.earnMoney(requestedAmount); | ||||
|  | ||||
|         // Transfer ownership of the properties from sender to receiver | ||||
|         // Transfer properties | ||||
|         if (offeredProperties != null) { | ||||
|             for (PropertyField property : offeredProperties) { | ||||
|                 transferProperty(sender, receiver, property); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Transfer ownership of the properties from receiver to sender | ||||
|         if (requestedProperties != null) { | ||||
|             for (PropertyField property : requestedProperties) { | ||||
|                 transferProperty(receiver, sender, property); | ||||
| @@ -76,73 +122,57 @@ public class TradeHandler { | ||||
|         transferJailCards(sender, receiver, offeredJailCards); | ||||
|         transferJailCards(receiver, sender, requestedJailCards); | ||||
|  | ||||
|         System.out.println("Trade accepted. " + sender.getName() + " and " + receiver.getName() + " completed the trade."); | ||||
|         System.out.println("Trade completed between " + sender.getName() + " and " + receiver.getName()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Rejects the trade offer. | ||||
|      * | ||||
|      * @param receiver the Player who is rejecting the trade | ||||
|      * Rejects the trade. | ||||
|      */ | ||||
|     public void rejectTrade(Player receiver) { | ||||
|         System.out.println("Trade rejected by " + receiver.getName()); | ||||
|     public void rejectTrade() { | ||||
|         System.out.println(receiver.getName() + " rejected the trade."); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Validates a trade offer by checking if the sender and receiver own the properties involved, | ||||
|      * have sufficient funds for the money involved in the trade, and have enough jail cards. | ||||
|      * Validates the trade offer by checking ownership, balances, and jail cards. | ||||
|      * | ||||
|      * @param sender             the Player initiating the trade | ||||
|      * @param offeredAmount      the amount of money the sender is offering | ||||
|      * @param offeredProperties  the list of properties the sender is offering | ||||
|      * @param offeredJailCards   the number of jail cards the sender is offering | ||||
|      * @param receiver           the Player receiving the trade offer | ||||
|      * @param requestedAmount    the amount of money the sender is requesting | ||||
|      * @param requestedProperties the list of properties the sender is requesting from the receiver | ||||
|      * @param requestedJailCards the number of jail cards the sender is requesting from the receiver | ||||
|      * @return true if the trade offer is valid, false otherwise | ||||
|      * @return true if the trade is valid, false otherwise | ||||
|      */ | ||||
|     private boolean validateTrade(Player sender, int offeredAmount, List<PropertyField> offeredProperties, int offeredJailCards, | ||||
|                                   Player receiver, int requestedAmount, List<PropertyField> requestedProperties, int requestedJailCards) { | ||||
|         // Check if sender has enough money to offer | ||||
|     private boolean validateTrade() { | ||||
|         // Validate sender's ability to offer money | ||||
|         if (sender.getAccountBalance() < offeredAmount) { | ||||
|             System.out.println("Sender does not have enough balance to make this offer."); | ||||
|             System.out.println("Sender does not have enough money to offer."); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // Check if receiver has enough money to offer | ||||
|         // Validate receiver's ability to fulfill the requested amount | ||||
|         if (receiver.getAccountBalance() < requestedAmount) { | ||||
|             System.out.println("Receiver does not have enough balance to fulfill requested amount."); | ||||
|             System.out.println("Receiver does not have enough money to fulfill the request."); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // Check if sender owns all the offered properties | ||||
|         // Validate property ownership | ||||
|         if (offeredProperties != null) { | ||||
|             for (PropertyField property : offeredProperties) { | ||||
|                 if (!sender.getProperties().contains(property)) { | ||||
|                     System.out.println("Sender does not own the property " + property.getName() + " being offered."); | ||||
|                     System.out.println("Sender does not own property: " + property.getName()); | ||||
|                     return false; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Check if receiver owns all the requested properties | ||||
|         if (requestedProperties != null) { | ||||
|             for (PropertyField property : requestedProperties) { | ||||
|                 if (!receiver.getProperties().contains(property)) { | ||||
|                     System.out.println("Receiver does not own the property " + property.getName() + " requested."); | ||||
|                     System.out.println("Receiver does not own property: " + property.getName()); | ||||
|                     return false; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Check if sender has enough jail cards to offer | ||||
|         // Validate jail cards | ||||
|         if (sender.getNumJailCard() < offeredJailCards) { | ||||
|             System.out.println("Sender does not have enough jail cards to offer."); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // Check if receiver has enough jail cards to fulfill the request | ||||
|         if (receiver.getNumJailCard() < requestedJailCards) { | ||||
|             System.out.println("Receiver does not have enough jail cards to fulfill the request."); | ||||
|             return false; | ||||
| @@ -152,17 +182,16 @@ public class TradeHandler { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Transfers a property from one player to another. | ||||
|      * Transfers a property between players. | ||||
|      * | ||||
|      * @param from     the Player transferring the property | ||||
|      * @param to       the Player receiving the property | ||||
|      * @param from the Player transferring the property | ||||
|      * @param to   the Player receiving the property | ||||
|      * @param property the PropertyField being transferred | ||||
|      */ | ||||
|     private void transferProperty(Player from, Player to, PropertyField property) { | ||||
|         from.sellProperty(property); | ||||
|         to.buyProperty(property); | ||||
|         property.setOwner(to); // Update the property's owner | ||||
|  | ||||
|         property.setOwner(to); | ||||
|         System.out.println("Property " + property.getName() + " transferred from " + from.getName() + " to " + to.getName()); | ||||
|     } | ||||
|  | ||||
| @@ -178,6 +207,6 @@ public class TradeHandler { | ||||
|             from.removeJailCard(); | ||||
|             to.addJailCard(); | ||||
|         } | ||||
|         System.out.println("Transferred " + numCards + " jail card(s) from " + from.getName() + " to " + to.getName()); | ||||
|         System.out.println(numCards + " jail card(s) transferred from " + from.getName() + " to " + to.getName()); | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user