Coverage Summary for Class: PlayCharacterCard (it.polimi.ingsw.Controller.Actions)

Class Class, % Method, % Branch, % Line, %
PlayCharacterCard 100% (1/1) 100% (4/4) 85,7% (12/14) 91,2% (31/34)


1 package it.polimi.ingsw.Controller.Actions; 2  3 import it.polimi.ingsw.Exceptions.Container.InvalidContainerIndexException; 4 import it.polimi.ingsw.Exceptions.Input.GenericInputValidationException; 5 import it.polimi.ingsw.Exceptions.Input.InputValidationException; 6 import it.polimi.ingsw.Exceptions.Input.InvalidElementException; 7 import it.polimi.ingsw.Misc.OptionalValue; 8 import it.polimi.ingsw.Misc.Pair; 9 import it.polimi.ingsw.Model.CharacterCard; 10 import it.polimi.ingsw.Model.CharacterCardInput; 11 import it.polimi.ingsw.Model.Enums.GameMode; 12 import it.polimi.ingsw.Model.Enums.GamePhase; 13 import it.polimi.ingsw.Model.Enums.PawnColour; 14 import it.polimi.ingsw.Model.Model; 15 import it.polimi.ingsw.Model.PlayerBoard; 16  17 import java.io.Serial; 18 import java.util.List; 19  20  21 public class PlayCharacterCard extends PlayerAction { 22  @Serial 23  private static final long serialVersionUID = 207L; // convention: 2 for controller, (01 -> 99) for objects 24  25  private final int selectedCard; // mandatory 26  private final OptionalValue<Integer> optTargetIsland; 27  private final OptionalValue<PawnColour> optTargetPawn; 28  private final OptionalValue<List<Pair<PawnColour, PawnColour>>> optTargetPawnPairs; 29  30  /** 31  * Create a new instance of this class with the following inputs: 32  * 33  * @param playerBoardId the ID of the current {@link PlayerBoard} 34  * @param selectedCard the ID of the {@link CharacterCard} to be played 35  * @param optTargetIsland if a target island is required as an input to the card, provide it here 36  * @param optTargetPawn if a target pawn is required as an input to the card, provide it here 37  * @param optTargetPawnPairs if a list of pawn pairs is required as an input to the card, provide it here 38  */ 39  public PlayCharacterCard( 40  int playerBoardId, 41  int selectedCard, 42  OptionalValue<Integer> optTargetIsland, 43  OptionalValue<PawnColour> optTargetPawn, 44  OptionalValue<List<Pair<PawnColour, PawnColour>>> optTargetPawnPairs) { 45  super(playerBoardId, true); 46  this.selectedCard = selectedCard; 47  this.optTargetIsland = optTargetIsland; 48  this.optTargetPawn = optTargetPawn; 49  this.optTargetPawnPairs = optTargetPawnPairs; 50  } 51  52  /** 53  * {@inheritDoc} 54  * <ul> 55  * <li>The {@link GameMode} must be {@link GameMode#ADVANCED}</li> 56  * <li>The {@link GamePhase} must be {@link GamePhase#ACTION}</li> 57  * <li>The selected character card must be within bounds (index 0 to 2 included)</li> 58  * <li>The player needs to have enough coins to by the card</li> 59  * <li>The {@link CharacterCardInput} generated from the attributes must be correct</li> 60  * </ul> 61  * 62  * @param history the controller stores a {@link List} of previous {@link PlayerAction}s related to the player taking 63  * the current turn (at every new turn, the history is cleared). 64  * Some actions may use this {@link List} to check for duplicates. 65  * @param ctx a reference to {@link Model}. Some actions may use this reference to check for consistency between what 66  * the actions declares and what the Model offers. 67  * @return An empty {@link OptionalValue} in case of a successful validation. Otherwise the returned {@link OptionalValue} 68  * contains the related {@link InputValidationException} 69  */ 70  @Override 71  protected OptionalValue<InputValidationException> customValidation(List<PlayerAction> history, Model ctx) { 72  if (ctx.getGameMode() != GameMode.ADVANCED) { 73  return OptionalValue.of(new GenericInputValidationException("Character Card", "can't be played in simple mode")); 74  } 75  PlayerBoard caller = ctx.getMutableTurnOrder().getMutableCurrentPlayer(); 76  if (ctx.getMutableTurnOrder().getGamePhase() != GamePhase.ACTION) { 77  return OptionalValue.of(new GenericInputValidationException("History", "the game is not in the correct phase")); 78  } 79  80  // generate the input object before validation 81  CharacterCardInput cardInput; 82  try { 83  cardInput = generateCharacterCardInput(caller, ctx); 84  } catch (InvalidContainerIndexException e) { 85  return OptionalValue.of(new InvalidElementException("Target Island")); 86  } 87  88  if (!(this.selectedCard >= 0 && this.selectedCard < 3)) { //selectedCard out of bounds 89  return OptionalValue.of(new InvalidElementException("Character Card")); 90  } 91  CharacterCard selectedCard = ctx.getCharacterCards().get(this.selectedCard); 92  if (caller.getCoinBalance() < selectedCard.getCost()) { 93  return OptionalValue.of(new GenericInputValidationException("Character Card", 94  "can't be played due to insufficient coin balance")); 95  } 96  97  return selectedCard.checkInput(cardInput); 98  } 99  100  @Override 101  public void unsafeExecute(Model ctx) throws Exception { 102  PlayerBoard caller = ctx.getMutableTurnOrder().getMutableCurrentPlayer(); 103  CharacterCard characterCard = ctx.getCharacterCards().get(this.selectedCard); 104  caller.payCharacterEffect(characterCard); 105  if (characterCard.getTimeUsed() > 0) { 106  ctx.addCoinToReserve(characterCard.getCost()); 107  } else { 108  ctx.addCoinToReserve(characterCard.getCost() - 1); //the first time, one coin has to be placed on the card and not in the coin reserve 109  } 110  characterCard.unsafeUseCard(generateCharacterCardInput(caller, ctx)); 111  } 112  113  /** 114  * a {@link CharacterCard} requires more information than the one contained in the constructor of this {@link PlayerAction} 115  * to be able to function. During the calls to verify the action, a {@link CharacterCardInput} object needs to be created so 116  * the internal validation mechanism of the {@link CharacterCard} can work. 117  * 118  * @param caller instead of the id of the current player, a reference to the current {@link PlayerBoard} is needed 119  * @param ctx the {@link Model} object is required to allow translation of the attributes of this object into a {@link CharacterCardInput} 120  * @return the {@link CharacterCardInput} associated with this object's attributes 121  * @throws InvalidContainerIndexException if the attribute of target island is pointing to an unidentified island-ID in the model, this exception is thrown 122  */ 123  private CharacterCardInput generateCharacterCardInput(PlayerBoard caller, Model ctx) throws InvalidContainerIndexException { 124  CharacterCardInput out = new CharacterCardInput(caller); 125  if (this.optTargetIsland.isPresent()) { 126  int id = this.optTargetIsland.get(); 127  out.setTargetIsland(ctx.getMutableIslandField().getMutableIslandById(id)); 128  } 129  this.optTargetPawn.ifPresent(out::setTargetPawn); 130  this.optTargetPawnPairs.ifPresent(out::setTargetPawnPairs); 131  return out; 132  } 133 }