1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.sf.magicproject.action;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.util.ArrayList;
24 import java.util.List;
25
26 import javax.swing.JOptionPane;
27
28 import net.sf.magicproject.action.context.ActionContextWrapper;
29 import net.sf.magicproject.action.context.Int;
30 import net.sf.magicproject.action.target.ChoosenTarget;
31 import net.sf.magicproject.clickable.ability.Ability;
32 import net.sf.magicproject.clickable.targetable.player.Player;
33 import net.sf.magicproject.event.context.ContextEventListener;
34 import net.sf.magicproject.expression.Expression;
35 import net.sf.magicproject.expression.ExpressionFactory;
36 import net.sf.magicproject.network.IdMessages;
37 import net.sf.magicproject.stack.StackManager;
38 import net.sf.magicproject.ui.i18n.LanguageManagerMDB;
39
40 /***
41 * Propose to choose within several valid actions list. If 'hop' attribute is
42 * specified and is 'true', cancel option would be available during the choice.
43 *
44 * @author <a href="mailto:fabdouglas@users.sourceforge.net">Fabrice Daugan </a>
45 * @since 0.85
46 * @since 0.86 non valid choices are not proposed
47 */
48 public class InputChoice extends MessagingAction {
49
50 /***
51 * Create an instance of InputChoice by reading a file Offset's file must
52 * pointing on the first byte of this action <br>
53 * <ul>
54 * Structure of InputStream : Data[size]
55 * <li>message [Msg]</li>
56 * <li>allow cancel [boolean]</li>
57 * <li>hop on cancel [Expression]</li>
58 * <li>action index [1]</li>
59 * <li>action is in effects [boolean]</li>
60 * <li>nb choices [1]</li>
61 * <li>size of choice #1 [int]</li>
62 * <li>size of choice #n [int]</li>
63 * <li>actions of choice #1 [Action[]]</li>
64 * <li>hop action value= SIGMA(i={2..n}, nb actions of choice i) [Expression]</li>
65 * <li>actions of choice #2 [Action[]]</li>
66 * <li>hop action value= SIGMA(i={m..n}, nb actions of choice i) [Expression]</li>
67 * </ul>
68 *
69 * @param inputFile
70 * file containing this action
71 * @throws IOException
72 * if error occurred during the reading process from the specified
73 * input stream
74 */
75 InputChoice(InputStream inputFile) throws IOException {
76 super(inputFile);
77 cancel = inputFile.read() == 1;
78 hopOnCancel = ExpressionFactory.readNextExpression(inputFile);
79 actionIndex = inputFile.read();
80 actionInEffects = inputFile.read() == 1;
81 nbActions = new int[inputFile.read()];
82 for (int i = 0; i < nbActions.length; i++) {
83 nbActions[i] = inputFile.read();
84 }
85 }
86
87 @Override
88 public final Actiontype getIdAction() {
89 return Actiontype.CHOICE;
90 }
91
92 @Override
93 public boolean play(ContextEventListener context, Ability ability) {
94 final List<String> actionListStr = getActionList(ability, context);
95 final Player controller = (Player) this.controller.getTargetable(ability,
96 context, null);
97
98 int startIndex = actionIndex + 1;
99 final MAction[] actionList = getActionList(ability);
100 boolean one = false;
101 for (int i = 0; i < actionListStr.size(); i++) {
102 if (isValidChoice(ability, actionList, startIndex, nbActions[i])) {
103 one = true;
104 } else {
105 actionListStr.set(i, null);
106 }
107 startIndex += 1 + nbActions[i];
108 }
109
110 if (one) {
111 controller.setActivePlayer();
112 if (controller.isYou()) {
113 replayAction(context, ability,
114 new net.sf.magicproject.ui.wizard.Choice(context, ability, this,
115 cancel, actionListStr));
116 }
117
118 } else {
119
120 StackManager.getInstance().finishSpell();
121 }
122 return false;
123 }
124
125 @Override
126 public final boolean replay(ActionContextWrapper actionContext,
127 ContextEventListener context, Ability ability) {
128 StackManager.actionManager.setHop(((Int) actionContext.actionContext)
129 .getInt());
130 return true;
131 }
132
133 @Override
134 protected final int getMessagingActionId() {
135 return IdMessages.MSG_CHOICE;
136 }
137
138 @Override
139 protected void setAnswer(int optionAnswer, int indexAnswer,
140 ContextEventListener context, Ability ability,
141 ActionContextWrapper actionContext) {
142 if (optionAnswer == JOptionPane.NO_OPTION) {
143
144 final int hopValue = hopOnCancel.getValue(
145 StackManager.actionManager.currentAbility, null, StackManager
146 .getInstance().getAbilityContext());
147 if (hopValue != 0) {
148 StackManager.actionManager.setHop(hopValue);
149 StackManager.resolveStack();
150 } else {
151 StackManager.getInstance().finishSpell();
152 }
153 } else {
154
155 int sum = 1;
156 for (int i = 0; i < indexAnswer; i++) {
157 sum += nbActions[i] + (i < indexAnswer - 1 ? 2 : 1);
158 }
159 StackManager.actionManager.setHop(sum);
160 if (actionContext != null) {
161 StackManager.actionManager.getActionContext().actionContext = new Int(
162 sum);
163 }
164 StackManager.resolveStack();
165 }
166 }
167
168 @Override
169 public String toString(Ability ability) {
170 return "choose";
171 }
172
173 @Override
174 public String toHtmlString(Ability ability, ContextEventListener context) {
175 if (actionName != null) {
176 if (actionName.charAt(0) == '%') {
177 return "";
178 }
179 return LanguageManagerMDB.getString(actionName);
180 }
181
182 final List<String> actionListStr = getActionList(ability, context);
183 String res = LanguageManagerMDB.getString("choose");
184 for (int i = actionListStr.size(); i-- > 0;) {
185 res += " - " + actionListStr.get(i);
186 }
187 return res;
188 }
189
190 /***
191 * Verify if this target action may cause abortion of the current ability.
192 *
193 * @param ability
194 * is the ability owning this action. The card component of this
195 * ability should correspond to the card owning this test too.
196 * @param actionIndex
197 * the choosen action index
198 * @return true if this target action may cause abortion of the current
199 * ability.
200 */
201 public final boolean checkTarget(Ability ability, int actionIndex) {
202 if (cancel) {
203 return true;
204 }
205 int choiceIndex = actionIndex + 1;
206 final MAction[] actionList = getActionList(ability);
207 for (int i = 0; i < nbActions.length; i++) {
208 if (!isValidChoice(ability, actionList, choiceIndex, nbActions[i])) {
209 return true;
210 }
211
212 choiceIndex += nbActions[i] + 1;
213 }
214
215 return false;
216 }
217
218 private boolean isValidChoice(Ability ability, MAction[] actionList,
219 int startIndex, int length) {
220 for (int j = startIndex; j < startIndex + length; j++) {
221 if (actionList[j] instanceof ChoosenTarget) {
222 if (j != 0
223 && actionList[j - 1].getIdAction() == Actiontype.REPEAT_ACTION
224 && !((ChoosenTarget) actionList[j]).checkTarget(ability,
225 ((Repeat) actionList[j - 1]).getPreemptionTimes(ability,
226 ability.getCard()))) {
227 return false;
228 }
229 if (!((ChoosenTarget) actionList[j]).checkTarget(ability, 1)) {
230 return false;
231 }
232 }
233 }
234 return true;
235 }
236
237 private MAction[] getActionList(Ability ability) {
238 if (actionInEffects) {
239 return ability.effectList();
240 }
241 return ability.actionList();
242 }
243
244 /***
245 * Return the hop to do to skip this action.
246 *
247 * @return the hop to do to skip this action.
248 */
249 public int getSkipHop() {
250 int res = nbActions.length - 1;
251 for (int i = nbActions.length; i-- > 0;) {
252 res += nbActions[i];
253 }
254 return res;
255 }
256
257 private List<String> getActionList(Ability ability,
258 ContextEventListener context) {
259 return getActionList(ability, getActionList(ability), actionIndex + 1,
260 context);
261 }
262
263 /***
264 * @see net.sf.magicproject.clickable.ability.UserAbility#toHtmlString()
265 * @see net.sf.magicproject.clickable.targetable.TriggeredCardChoice#getActionList(Ability,
266 * MAction[], int)
267 * @see java.awt.Choice#getActionList(Ability)
268 */
269 private List<String> getActionList(Ability ability, MAction[] actionList,
270 int pStartIndex, ContextEventListener context) {
271 final List<String> res = new ArrayList<String>();
272 int startIndex = pStartIndex;
273
274 for (int j = 0; j < nbActions.length; j++) {
275 String msg = null;
276 final int end = startIndex + nbActions[j];
277 for (int id = startIndex; id < end; id++) {
278 final MAction action = actionList[id];
279 String str = null;
280 if (action.getIdAction() == Actiontype.REPEAT_ACTION) {
281 str = actionList[++id].toHtmlString(ability, ((Repeat) action)
282 .getPreemptionTimes(ability, null), context);
283 } else {
284 str = action.toHtmlString(ability, context);
285 }
286 if (str != null && str.length() > 0) {
287 if (msg == null) {
288 msg = str;
289 } else {
290 msg += ", " + str;
291 }
292 }
293 }
294 res.add(msg);
295
296 startIndex = end + 1;
297 }
298 return res;
299 }
300
301 /***
302 * Nb actions per choice. Hop actions are not included.
303 */
304 private final int[] nbActions;
305
306 /***
307 * Allow cancel?
308 */
309 private final boolean cancel;
310
311 /***
312 * Index of this action within the ability : effects or cost
313 */
314 private final int actionIndex;
315
316 /***
317 * Is this action is in effects?
318 */
319 private final boolean actionInEffects;
320
321 /***
322 * Optional non zero jump to do when 'cancel' is pressed. Can only be
323 * specified when 'cancel' is allowed. When this attribute is not specified,
324 * 'cancel' cause the ability to be finished.
325 */
326 private final Expression hopOnCancel;
327 }