1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
77 eventComing = EventFactory.readNextEvent(inputFile, card);
78
79
80 actionList = ActionFactory.readActionList(inputFile, null);
81
82
83 eventComing.test = ActionFactory.getConstraints(actionList,
84 eventComing.test);
85
86
87 updateCheckingAction();
88
89
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
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
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
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
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
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
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
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
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
307 StackManager.actionManager
308 .succeedClickOn(((WaitingAbility) StackManager.actionManager.currentAction)
309 .advancedAbilitiesOf(card).get(index - 128));
310 } else {
311
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 }