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   */
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  				// Ignore this event
77  			}
78  
79  			public void ancestorResized(HierarchyEvent evt) {
80  				updatePanel();
81  			}
82  		});
83  		// updateSize();
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 			// only one triggered ability to choose.
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 		// the TBZ contains abstract abilities, first play the highest priority
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 			 * The triggered does not verify the second test, we release the reference
262 			 * to card timestamp locked by context of this triggered.
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 		// the TBZ contains abstract abilities, first play the highest priority
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 			 * The triggered does not verify the second test, we release the reference
295 			 * to card timestamp locked by context of this triggered.
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 		// the TBZ contains abstract abilities, first play the highest priority
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 			 * The triggered does not verify the second test, we release the reference
327 			 * to card timestamp locked by context of this triggered.
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 }