1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package net.sf.magicproject.zone;
21
22 import java.awt.Component;
23 import java.awt.FlowLayout;
24 import java.awt.event.HierarchyBoundsListener;
25 import java.awt.event.HierarchyEvent;
26 import java.util.ArrayList;
27 import java.util.List;
28
29 import javax.swing.JScrollPane;
30
31 import net.sf.magicproject.clickable.ability.Ability;
32 import net.sf.magicproject.clickable.targetable.card.MCard;
33 import net.sf.magicproject.clickable.targetable.card.TriggeredCard;
34 import net.sf.magicproject.event.context.ContextEventListener;
35 import net.sf.magicproject.stack.StackManager;
36 import net.sf.magicproject.token.IdZones;
37 import net.sf.magicproject.token.Visibility;
38 import net.sf.magicproject.tools.Log;
39 import net.sf.magicproject.tools.MToolKit;
40
41 /***
42 * @author <a href="mailto:fabdouglas@users.sourceforge.net">Fabrice Daugan </a>
43 * @since 0.54.17
44 * @since 0.60.20 this zone conatains now a special abstract place where
45 * abstract triggered abilities are place when they are awakened.
46 * Abilities placed into this zone are resolved prior the normal ones
47 */
48 public class TriggeredBuffer extends MZone {
49
50 /***
51 * The zone name.
52 */
53 public static final String ZONE_NAME = "tbz";
54
55 /***
56 * create a new instance of TriggeredBuffer
57 *
58 * @param superPanel
59 * scroll panel containing this panel
60 * @param you
61 * is this zone is controlled by you.
62 * @since 0.3 feature "reverseImage" implemented
63 * @since 0.80 feature "reverseImage" removed, since this panel has been moved
64 * in a JTabbedPanel with the stack
65 * @since 0.80 Support the 'highlight' feature
66 * @see IdZones
67 */
68 TriggeredBuffer(JScrollPane superPanel, boolean you) {
69 super(IdZones.TRIGGERED, new FlowLayout(FlowLayout.CENTER, 2, 2),
70 superPanel, !you, ZONE_NAME);
71 this.you = you;
72 this.reverseImage = false;
73 addHierarchyBoundsListener(new HierarchyBoundsListener() {
74
75 public void ancestorMoved(HierarchyEvent evt) {
76
77 }
78
79 public void ancestorResized(HierarchyEvent evt) {
80 updatePanel();
81 }
82 });
83
84 setAutoscrolls(true);
85 superPanel.setViewportView(this);
86 visibility = Visibility.PUBLIC;
87 }
88
89 /***
90 * Return the first ability with the 'high-priority' tag, or the triggered
91 * ability without this tag, or null if there are several triggered ability
92 * with the same priority and if the 'auto-stack' option is disabled.The
93 * 'auto-stack' order is FIFO.
94 *
95 * @return the first ability with the 'high-priority' tag. Return null value
96 * if none was found.
97 * @since 0.71 option 'auto-stack' has been added. In case of several
98 * triggered cards have the same priority, the play choose the first
99 * one and stack it automatically.
100 */
101 public TriggeredCard chooseAbility() {
102 Component[] cps = this.getComponents();
103 for (int i = 0; i < cps.length; i++) {
104 TriggeredCard triggered = (TriggeredCard) cps[i];
105 Ability ability = triggered.triggeredAbility;
106 int oldActivePlayer = StackManager.idActivePlayer;
107 StackManager.idActivePlayer = triggered.controller.idPlayer;
108 if (ability.hasHighPriority()) {
109 if (ability.eventComing().reCheck(triggered)) {
110 StackManager.idActivePlayer = oldActivePlayer;
111 return triggered;
112 }
113 StackManager.idActivePlayer = oldActivePlayer;
114 removeTriggered(triggered);
115 return chooseAbility();
116 }
117 if (!ability.eventComing().reCheck(triggered)) {
118 removeTriggered(triggered);
119 }
120 StackManager.idActivePlayer = oldActivePlayer;
121 }
122 if (this.getComponentCount() == 1) {
123
124 return (TriggeredCard) getComponent(0);
125 }
126 return null;
127 }
128
129 /***
130 * Removes the specified component from this container. This method also
131 * notifies the layout manager to remove the component from this container's
132 * layout via the <code>removeLayoutComponent</code> method.
133 *
134 * @param triggered
135 * the component to be removed
136 */
137 public void removeTriggered(TriggeredCard triggered) {
138 super.remove(triggered);
139 }
140
141 /***
142 * Removes the specified component from this container. This method also
143 * notifies the layout manager to remove the component from this container's
144 * layout via the <code>removeLayoutComponent</code> method.
145 *
146 * @param comp
147 * the component to be removed
148 * @deprecated use 'removeTriggered(MTriggeredCard)' instead
149 */
150 @Override
151 @Deprecated
152 public void remove(Component comp) {
153 throw new InternalError(
154 "@deprecated, use 'removeTriggered(MTriggeredCard)' instead.");
155 }
156
157 /***
158 * update this hand
159 */
160 @Override
161 public void updatePanel() {
162 superPanel.setViewportView(this);
163 setPreferredSize(getLayout().preferredLayoutSize(this));
164 }
165
166 /***
167 * Add a card to this zone.
168 *
169 * @param card
170 * the card to add
171 */
172 @Override
173 public void addTop(MCard card) {
174 card.reverseAsNeeded();
175 super.addBottom(card);
176 }
177
178 /***
179 * Add the given ability to the special zone 'abstract triggered'. This zone
180 * is resolved prior to the standard abilities
181 *
182 * @param ability
183 * the ability (should be an abstract one) toe the special zone
184 * 'abstract triggered'
185 * @param context
186 * the associated context to this ability. This context wouldbe
187 * restored when this ability would be played. Commonly this context
188 * contains information that was looked for by the event of this
189 * ability.
190 */
191 public void addHidden(Ability ability, ContextEventListener context) {
192 if (!ability.isHidden()) {
193 Log.error("This ability has not the hidden tag as normal, "
194 + "resolve will be forced");
195 }
196 if (ability.optimizer
197 .addTo(ability, context, ability.priority.getAbstractZone(
198 abstractLowestZone, abstractZone, abstractHighestZone))) {
199 Log.debug("added to TBZ : " + ability
200 + MToolKit.getLogCardInfo(ability.getCard()));
201 }
202 }
203
204 @Override
205 public void add(Component comp, Object constraints) {
206 throw new InternalError("should not be called");
207 }
208
209 /***
210 * Add the triggered ability to this zone and updatet this zone.
211 *
212 * @param triggeredCard
213 * the triggered card to this zone
214 */
215 public void add(TriggeredCard triggeredCard) {
216 Log.debug("added to TBZ : added untagged triggered ability "
217 + triggeredCard.triggeredAbility.getName() + ", Tcard="
218 + triggeredCard.getCardName()
219 + MToolKit.getLogCardInfo(triggeredCard.triggeredAbility.getCard()));
220 if (triggeredCard.triggeredAbility.isHidden()) {
221 throw new InternalError("ability shouldn't be tagged as hidden");
222 }
223 super.add(triggeredCard);
224 updatePanel();
225 }
226
227 /***
228 * Return the triggered abilities positionned at the specified index
229 *
230 * @param index
231 * is the index where is the element
232 * @return the triggered abilities at the specified index
233 */
234 public TriggeredCard getTriggeredAbility(int index) {
235 return (TriggeredCard) getComponent(index);
236 }
237
238 /***
239 * Add to the stack the first abstract triggered abilities present into the
240 * abstract place, and resolve it since this one should have the 'hidden' tag.
241 * The returned value is true if the abstract zone contained at least one
242 * triggered ability.
243 *
244 * @param tmpIdPlayer
245 * the player that would get virtually priority.
246 * @return true if one of the abstract places contained at least a triggered
247 * ability. Return false if no hidden ability was found.
248 */
249 public boolean resolveHiddenHighLevel(int tmpIdPlayer) {
250
251 while (!abstractHighestZone.isEmpty()) {
252 final TriggeredCard triggered = abstractHighestZone.remove(0);
253 final int oldActivePlayer = StackManager.idActivePlayer;
254 StackManager.idActivePlayer = triggered.controller.idPlayer;
255 if (triggered.triggeredAbility.eventComing().reCheck(triggered)) {
256 StackManager.idHandedPlayer = tmpIdPlayer;
257 StackManager.actionManager.succeedClickOn(triggered);
258 return true;
259 }
260
261
262
263
264 triggered.getAbilityContext().removeTimestamp();
265 StackManager.idActivePlayer = oldActivePlayer;
266 }
267 return false;
268 }
269
270 /***
271 * Add to the stack the first abstract triggered abilities present into the
272 * abstract place, and resolve it since this one should have the 'hidden' tag.
273 * The returned value is true if the abstract zone contained at least one
274 * triggered ability.
275 *
276 * @param tmpIdPlayer
277 * the player that would get virtually priority.
278 * @return true if one of the abstract places contained at least a triggered
279 * ability. Return false if no hidden ability was found.
280 */
281 public boolean resolveHiddenNormalLevel(int tmpIdPlayer) {
282
283 while (!abstractZone.isEmpty()) {
284 final TriggeredCard triggered = abstractZone.remove(0);
285 final int oldActivePlayer = StackManager.idActivePlayer;
286 StackManager.idActivePlayer = triggered.controller.idPlayer;
287 if (triggered.triggeredAbility.eventComing().reCheck(triggered)) {
288 StackManager.idActivePlayer = oldActivePlayer;
289 StackManager.idHandedPlayer = tmpIdPlayer;
290 StackManager.actionManager.succeedClickOn(triggered);
291 return true;
292 }
293
294
295
296
297 triggered.getAbilityContext().removeTimestamp();
298 StackManager.idActivePlayer = oldActivePlayer;
299 }
300 return false;
301 }
302
303 /***
304 * Add to the stack the first abstract triggered abilities present into the
305 * abstract place, and resolve it since this one should have the 'hidden' tag.
306 * The returned value is true if the abstract zone contained at least one
307 * triggered ability.
308 *
309 * @param tmpIdPlayer
310 * the player that would get virtually priority.
311 * @return true if one of the abstract places contained at least a triggered
312 * ability. Return false if no hidden ability was found.
313 */
314 public boolean resolveHiddenLowestLevel(int tmpIdPlayer) {
315
316 while (!abstractLowestZone.isEmpty()) {
317 final TriggeredCard triggered = abstractLowestZone.remove(0);
318 final int oldActivePlayer = StackManager.idActivePlayer;
319 StackManager.idActivePlayer = triggered.controller.idPlayer;
320 if (triggered.triggeredAbility.eventComing().reCheck(triggered)) {
321 StackManager.idHandedPlayer = tmpIdPlayer;
322 StackManager.actionManager.succeedClickOn(triggered);
323 return true;
324 }
325
326
327
328
329 triggered.getAbilityContext().removeTimestamp();
330 StackManager.idActivePlayer = oldActivePlayer;
331 }
332 return false;
333 }
334
335 /***
336 * Highlight cards of this container with the STACKABLE color.
337 *
338 * @see net.sf.magicproject.clickable.targetable.card.TriggeredCard#highlightStackable()
339 */
340 public void highlightStackable() {
341 for (int i = getComponentCount(); i-- > 0;) {
342 ((TriggeredCard) getComponent(i)).highlightStackable();
343 }
344 highLight(TriggeredCard.STACKABLE_COLOR);
345 }
346
347 @Override
348 public int getControllerIdPlayer() {
349 return you ? 0 : 1;
350 }
351
352 @Override
353 public boolean isMustBePaintedReversed(MCard card) {
354 return false;
355 }
356
357 /***
358 * Represents the abstract zone where abstract abilities are placed
359 */
360 private List<TriggeredCard> abstractZone = new ArrayList<TriggeredCard>();
361
362 /***
363 * Represents the abstract zone where abstract abilities with the lowest
364 * priority are placed
365 */
366 private List<TriggeredCard> abstractLowestZone = new ArrayList<TriggeredCard>();
367
368 /***
369 * Represents the abstract zone where abstract abilities with the highest
370 * priority are placed
371 */
372 private List<TriggeredCard> abstractHighestZone = new ArrayList<TriggeredCard>();
373
374 /***
375 * is this zone is controlled by you.
376 */
377 private boolean you;
378 }