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 }