View Javadoc

1   /*
2    * Created on 27 octobre 2002, 12:15
3    * 
4    *   Magic-Project is a turn based strategy simulator
5    *   Copyright (C) 2003-2007 Fabrice Daugan
6    *
7    *   This program is free software; you can redistribute it and/or modify it 
8    * under the terms of the GNU General Public License as published by the Free 
9    * Software Foundation; either version 2 of the License, or (at your option) any
10   * later version.
11   *
12   *   This program is distributed in the hope that it will be useful, but WITHOUT 
13   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14   * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more 
15   * details.
16   *
17   *   You should have received a copy of the GNU General Public License along  
18   * with this program; if not, write to the Free Software Foundation, Inc., 
19   * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20   * 
21   */
22  package net.sf.magicproject.clickable.ability;
23  
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.util.Collection;
27  
28  import net.sf.magicproject.action.ActionFactory;
29  import net.sf.magicproject.action.Actiontype;
30  import net.sf.magicproject.action.MAction;
31  import net.sf.magicproject.action.RemoveObject;
32  import net.sf.magicproject.action.Repeat;
33  import net.sf.magicproject.action.listener.WaitingAbility;
34  import net.sf.magicproject.action.target.AbstractTarget;
35  import net.sf.magicproject.action.target.ChoosenTarget;
36  import net.sf.magicproject.clickable.action.ToStringHelper;
37  import net.sf.magicproject.clickable.targetable.card.MCard;
38  import net.sf.magicproject.event.EventFactory;
39  import net.sf.magicproject.event.MEventListener;
40  import net.sf.magicproject.event.context.ContextEventListener;
41  import net.sf.magicproject.network.ConnectionManager;
42  import net.sf.magicproject.network.IdMessages;
43  import net.sf.magicproject.stack.StackManager;
44  import net.sf.magicproject.token.IdCommonToken;
45  import net.sf.magicproject.token.TrueFalseAuto;
46  import net.sf.magicproject.tools.Log;
47  
48  /***
49   * A non-abstract ability.
50   * 
51   * @author <a href="mailto:fabdouglas@users.sourceforge.net">Fabrice Daugan </a>
52   */
53  public abstract class UserAbility extends Ability {
54  
55  	/***
56  	 * Create an instance of UserAbility <br>
57  	 * <ul>
58  	 * Structure of InputStream : Data[size]
59  	 * <li>event [Event]</li>
60  	 * <li>cost [Action[]]</li>
61  	 * <li>effect [Action[]]</li>
62  	 * </ul>
63  	 * 
64  	 * @param inputFile
65  	 *          file containing this ability
66  	 * @param card
67  	 *          referenced card
68  	 * @throws IOException
69  	 *           if error occurred during the reading process from the specified
70  	 *           input stream
71  	 * @since 0.81 constraints on actions of this ability are supported
72  	 */
73  	public UserAbility(InputStream inputFile, MCard card) throws IOException {
74  		super(inputFile);
75  		try {
76  			// Read id event
77  			eventComing = EventFactory.readNextEvent(inputFile, card);
78  
79  			// Read list of actions
80  			actionList = ActionFactory.readActionList(inputFile, null);
81  
82  			// Extract constraints
83  			eventComing.test = ActionFactory.getConstraints(actionList,
84  					eventComing.test);
85  
86  			// Optimize target options
87  			updateCheckingAction();
88  
89  			// Read list of effect
90  			effectList = ActionFactory.readActionList(inputFile, null);
91  		} catch (Throwable e) {
92  			throw new RuntimeException("reading UserAbility " + getName() + ", card:"
93  					+ card.getName(), e);
94  		}
95  	}
96  
97  	/***
98  	 * Create an instance of UserAbility
99  	 * 
100 	 * @param name
101 	 *          Name of card used to display this ability in a stack
102 	 * @param actionList
103 	 *          list of actions to do for activate this ability
104 	 * @param effectList
105 	 *          list of effects of this ability
106 	 * @param optimizer
107 	 *          the optimizer to use.
108 	 * @param priority
109 	 *          the resolution type.
110 	 * @param eventComing
111 	 *          event condition of this ability
112 	 * @param pictureName
113 	 *          the picture name of this ability. If <code>null</code> the card
114 	 *          picture will be used instead.
115 	 * @param linkedAbilities
116 	 *          the linked abilities. May be null.
117 	 * @param playAsSpell
118 	 *          play-as-spell.
119 	 */
120 	protected UserAbility(String name, MAction[] actionList,
121 			MAction[] effectList, Optimization optimizer, Priority priority,
122 			MEventListener eventComing, String pictureName,
123 			Collection<Ability> linkedAbilities, TrueFalseAuto playAsSpell) {
124 		super(name, optimizer, priority, pictureName, linkedAbilities, playAsSpell);
125 		this.actionList = actionList;
126 		this.effectList = effectList;
127 		this.eventComing = eventComing;
128 		updateCheckingAction();
129 	}
130 
131 	private void updateCheckingAction() {
132 		// Optimize target options
133 		checkingOptions = NO_TARGET;
134 		for (int i = actionList.length; i-- > 0;) {
135 			if (actionList()[i] instanceof ChoosenTarget) {
136 				if (((ChoosenTarget) actionList()[i]).isTargeting()) {
137 					checkingOptions |= REAL_TARGET;
138 				} else {
139 					checkingOptions |= SILENT_TARGET;
140 				}
141 			} else if (actionList()[i] instanceof RemoveObject && i > 0) {
142 				if (actionList()[i - 1] instanceof Repeat) {
143 					if (i > 1
144 							&& actionList()[i - 2] instanceof AbstractTarget
145 							&& ((Repeat) actionList()[i - 1]).getPreemptionTimes(this, null) > 0) {
146 						checkingOptions |= REMOVE_OBJECT;
147 					}
148 				} else if (actionList()[i - 1] instanceof AbstractTarget) {
149 					checkingOptions |= REMOVE_OBJECT;
150 				}
151 			}
152 		}
153 	}
154 
155 	@Override
156 	public abstract Ability clone(MCard container);
157 
158 	@Override
159 	public MCard getCard() {
160 		return eventComing.card;
161 	}
162 
163 	@Override
164 	public final boolean checkTargetActions() {
165 		return (checkingOptions & TARGET_OPTIONS) == NO_TARGET
166 				|| super.checkTargetActions();
167 	}
168 
169 	@Override
170 	public final boolean checkObjectActions() {
171 		return (checkingOptions & OBJECT_OPTIONS) == NO_TARGET
172 				|| super.checkObjectActions();
173 	}
174 
175 	@Override
176 	public final boolean recheckTargets() {
177 		if ((checkingOptions & TARGET_OPTIONS) == REAL_TARGET) {
178 			return super.recheckTargets();
179 		}
180 		return true;
181 	}
182 
183 	@Override
184 	public boolean isMatching() {
185 
186 		// enougth mana?
187 		final int[] manaNeeded = manaNeeded(null);
188 		int unUsed = 0;
189 		for (int i = IdCommonToken.COLOR_NAMES.length; i-- > IdCommonToken.BLACK_MANA;) {
190 			if (manaNeeded[i] > getController().mana.getMana(i, this)) {
191 				return false;
192 			}
193 			unUsed += getController().mana.getMana(i, this) - manaNeeded[i];
194 		}
195 		if (manaNeeded[IdCommonToken.COLORLESS_MANA] > getController().mana
196 				.getMana(IdCommonToken.COLORLESS_MANA, this)
197 				+ unUsed) {
198 			return false;
199 		}
200 
201 		// check the target actions of 'cost' part can be played
202 		return checkTargetActions() && checkObjectActions();
203 	}
204 
205 	@Override
206 	public int[] manaNeeded(ContextEventListener context) {
207 		final int[] result = new int[IdCommonToken.COLOR_NAMES.length];
208 		for (int id = actionList().length; id-- > 0;) {
209 			final MAction action = actionList[id];
210 			if (action.getIdAction() == Actiontype.PAY_MANA) {
211 				// this ability need manas
212 				final int[] actionCost = action.manaNeeded(this, context);
213 				for (int i = IdCommonToken.COLOR_NAMES.length; i-- > IdCommonToken.COLORLESS_MANA;) {
214 					result[i] += actionCost[i];
215 				}
216 			}
217 		}
218 		for (int id = effectList().length; id-- > 0;) {
219 			final MAction action = effectList()[id];
220 			if (action.getIdAction() == Actiontype.PAY_MANA) {
221 				// this ability need manas
222 				final int[] actionCost = action.manaNeeded(this, context);
223 				for (int i = IdCommonToken.COLOR_NAMES.length; i-- > IdCommonToken.COLORLESS_MANA;) {
224 					result[i] += actionCost[i];
225 				}
226 			}
227 		}
228 		return result;
229 	}
230 
231 	@Override
232 	public final String toString() {
233 		return ToStringHelper.toString(this);
234 	}
235 
236 	@Override
237 	public String toHtmlString(ContextEventListener context) {
238 		return ToStringHelper.toHtmlString(this, context);
239 	}
240 
241 	@Override
242 	public final MEventListener eventComing() {
243 		return eventComing;
244 	}
245 
246 	@Override
247 	public final MAction[] actionList() {
248 		return actionList;
249 	}
250 
251 	@Override
252 	public final MAction[] effectList() {
253 		return effectList;
254 	}
255 
256 	@Override
257 	public boolean triggerIt(ContextEventListener context) {
258 		super.triggerIt(context);
259 		if (isHidden()) {
260 			getController().zoneManager.triggeredBuffer.addHidden(this, context);
261 		} else {
262 			getController().zoneManager.triggeredBuffer
263 					.add(getTriggeredClone(context));
264 		}
265 		return true;
266 	}
267 
268 	/***
269 	 * is called when you click on me
270 	 * 
271 	 * @param index
272 	 *          is the index of this ability within the choice list of playable
273 	 *          abilities of the card owning this ability.
274 	 */
275 	public final void mouseClicked(int index) {
276 		if (StackManager.actionManager.clickOn(this)) {
277 			Log.debug("clickOn(Mouse):" + getCard() + " -> " + this);
278 			// send this information to our opponent : card + ability
279 			final int[] cardBytes = getCard().getBytes();
280 			final int[] toSend = new int[1 + cardBytes.length];
281 			System.arraycopy(cardBytes, 1, toSend, 1, cardBytes.length - 1);
282 			toSend[0] = IdMessages.MSG_CLICK_ABILITY;
283 			toSend[cardBytes.length] = index;
284 			// we inform opponent our choice
285 			ConnectionManager.sendToOpponent(toSend);
286 			StackManager.actionManager.succeedClickOn(this);
287 		}
288 	}
289 
290 	/***
291 	 * This method is invoked when opponent has clicked on this object. this call
292 	 * should be done from the net.sf.magicproject.network listener
293 	 * 
294 	 * @param input
295 	 *          input stream of our net.sf.magicproject.network connection
296 	 * @throws IOException
297 	 *           if error occurred when reading the message
298 	 */
299 	public static void clickOn(InputStream input) throws IOException {
300 		// waiting for card information
301 		final MCard card = MCard.getCard(input);
302 		Log.debug("clickOn(VirtualInput):" + card);
303 
304 		final int index = input.read();
305 		if (index >= 128) {
306 			// advanced activated ability
307 			StackManager.actionManager
308 					.succeedClickOn(((WaitingAbility) StackManager.actionManager.currentAction)
309 							.advancedAbilitiesOf(card).get(index - 128));
310 		} else {
311 			// normal activated ability
312 			StackManager.actionManager
313 					.succeedClickOn(((WaitingAbility) StackManager.actionManager.currentAction)
314 							.abilitiesOf(card).get(index));
315 		}
316 	}
317 
318 	/***
319 	 * will contains MAction objects of cost part
320 	 */
321 	protected MAction[] actionList;
322 
323 	/***
324 	 * will contains MAction objects of effect part
325 	 */
326 	protected MAction[] effectList;
327 
328 	private static final int REAL_TARGET = 2;
329 
330 	private static final int SILENT_TARGET = 1;
331 
332 	private static final int NO_TARGET = 0;
333 
334 	private static final int TARGET_OPTIONS = 3;
335 
336 	private static final int OBJECT_OPTIONS = 4;
337 
338 	private static final int REMOVE_OBJECT = 4;
339 
340 	/***
341 	 * Is this ability contains targeting or removing object action(s).
342 	 */
343 	protected int checkingOptions;
344 
345 }