View Javadoc

1   /*
2    *   Magic-Project is a turn based strategy simulator
3    *   Copyright (C) 2003-2007 Fabrice Daugan
4    *
5    *   This program is free software; you can redistribute it and/or modify it 
6    * under the terms of the GNU General Public License as published by the Free 
7    * Software Foundation; either version 2 of the License, or (at your option) any
8    * later version.
9    *
10   *   This program is distributed in the hope that it will be useful, but WITHOUT 
11   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12   * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more 
13   * details.
14   *
15   *   You should have received a copy of the GNU General Public License along  
16   * with this program; if not, write to the Free Software Foundation, Inc., 
17   * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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 			// opponent waits for our answer
118 		} else {
119 			// No available choice, so cancel option is choosen automatically
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 			// cancel has been choosen
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 			// choice has been made
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 		// we return only the string representation
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 			// next choice, skipping hidden hop action
212 			choiceIndex += nbActions[i] + 1;
213 		}
214 		// no valid choice
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 		// first, identify
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 			// skip the hop action
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 }