diff --git a/src/java/com/threerings/parlor/card/client/CardGameController.java b/src/java/com/threerings/parlor/card/client/CardGameController.java
index 95908d3af..9dae4d13a 100644
--- a/src/java/com/threerings/parlor/card/client/CardGameController.java
+++ b/src/java/com/threerings/parlor/card/client/CardGameController.java
@@ -21,20 +21,22 @@
package com.threerings.parlor.card.client;
+import com.threerings.util.Name;
+
import com.threerings.crowd.data.PlaceObject;
import com.threerings.parlor.card.data.Card;
import com.threerings.parlor.card.data.CardCodes;
import com.threerings.parlor.card.data.Hand;
-
import com.threerings.parlor.game.client.GameController;
+import com.threerings.parlor.turn.client.TurnGameController;
/**
* A controller class for card games. Handles common functions like
* accepting dealt hands.
*/
public abstract class CardGameController extends GameController
- implements CardCodes, CardGameReceiver
+ implements TurnGameController, CardCodes, CardGameReceiver
{
// Documentation inherited.
public void willEnterPlace (PlaceObject plobj)
@@ -54,6 +56,10 @@ public abstract class CardGameController extends GameController
CardGameDecoder.RECEIVER_CODE);
}
+ // Documentation inherited.
+ public void turnDidChange (Name turnHolder)
+ {}
+
/**
* Called when the server deals the client a new hand of cards. Default
* implementation does nothing.
@@ -72,4 +78,27 @@ public abstract class CardGameController extends GameController
*/
public void receivedCardsFromPlayer (int plidx, Card[] cards)
{}
+
+ /**
+ * Dispatched to the client when the server has forced it to send
+ * a set of cards to another player. Default implementation does
+ * nothing.
+ *
+ * @param plidx the index of the player to which the cards were sent
+ * @param cards the cards sent
+ */
+ public void sentCardsToPlayer (int plidx, Card[] cards)
+ {}
+
+ /**
+ * Dispatched to the client when a set of cards is transferred between
+ * two other players in the game. Default implementation does nothing.
+ *
+ * @param fromidx the index of the player sending the cards
+ * @param toidx the index of the player receiving the cards
+ * @param cards the number of cards transferred
+ */
+ public void cardsTransferredBetweenPlayers (int fromidx, int toidx,
+ int cards)
+ {}
}
diff --git a/src/java/com/threerings/parlor/card/client/CardGameDecoder.java b/src/java/com/threerings/parlor/card/client/CardGameDecoder.java
index 0645a4e3a..e750ee708 100644
--- a/src/java/com/threerings/parlor/card/client/CardGameDecoder.java
+++ b/src/java/com/threerings/parlor/card/client/CardGameDecoder.java
@@ -24,6 +24,14 @@ public class CardGameDecoder extends InvocationDecoder
* notifications. */
public static final int RECEIVED_CARDS_FROM_PLAYER = 2;
+ /** The method id used to dispatch {@link CardGameReceiver#sentCardsToPlayer}
+ * notifications. */
+ public static final int SENT_CARDS_TO_PLAYER = 3;
+
+ /** The method id used to dispatch {@link CardGameReceiver#cardsTransferredBetweenPlayers}
+ * notifications. */
+ public static final int CARDS_TRANSFERRED_BETWEEN_PLAYERS = 4;
+
/**
* Creates a decoder that may be registered to dispatch invocation
* service notifications to the specified receiver.
@@ -55,6 +63,18 @@ public class CardGameDecoder extends InvocationDecoder
);
return;
+ case SENT_CARDS_TO_PLAYER:
+ ((CardGameReceiver)receiver).sentCardsToPlayer(
+ ((Integer)args[0]).intValue(), (Card[])args[1]
+ );
+ return;
+
+ case CARDS_TRANSFERRED_BETWEEN_PLAYERS:
+ ((CardGameReceiver)receiver).cardsTransferredBetweenPlayers(
+ ((Integer)args[0]).intValue(), ((Integer)args[1]).intValue(), ((Integer)args[2]).intValue()
+ );
+ return;
+
default:
super.dispatchNotification(methodId, args);
}
diff --git a/src/java/com/threerings/parlor/card/client/CardGameReceiver.java b/src/java/com/threerings/parlor/card/client/CardGameReceiver.java
index 9be934aa6..cfcc83262 100644
--- a/src/java/com/threerings/parlor/card/client/CardGameReceiver.java
+++ b/src/java/com/threerings/parlor/card/client/CardGameReceiver.java
@@ -47,4 +47,24 @@ public interface CardGameReceiver extends InvocationReceiver
* @param cards the cards received
*/
public void receivedCardsFromPlayer (int plidx, Card[] cards);
+
+ /**
+ * Dispatched to the client when the server has forced it to send
+ * a set of cards to another player.
+ *
+ * @param plidx the index of the player to which the cards were sent
+ * @param cards the cards sent
+ */
+ public void sentCardsToPlayer (int plidx, Card[] cards);
+
+ /**
+ * Dispatched to the client when a set of cards is transferred between
+ * two other players in the game.
+ *
+ * @param fromidx the index of the player sending the cards
+ * @param toidx the index of the player receiving the cards
+ * @param cards the number of cards transferred
+ */
+ public void cardsTransferredBetweenPlayers (int fromidx, int toidx,
+ int cards);
}
diff --git a/src/java/com/threerings/parlor/card/client/CardGameService.java b/src/java/com/threerings/parlor/card/client/CardGameService.java
deleted file mode 100644
index 1aa9ae3f8..000000000
--- a/src/java/com/threerings/parlor/card/client/CardGameService.java
+++ /dev/null
@@ -1,26 +0,0 @@
-//
-// $Id: RoisterService.java 17829 2004-11-12 20:24:43Z mdb $
-
-package com.threerings.parlor.card.client;
-
-import com.threerings.parlor.card.data.Card;
-
-import com.threerings.presents.client.Client;
-import com.threerings.presents.client.InvocationService;
-
-/**
- * Service calls related to card games.
- */
-public interface CardGameService extends InvocationService
-{
- /**
- * Sends a set of cards to another player.
- *
- * @param client the client object
- * @param playerIndex the index of the player to receive the cards
- * @param cards the cards to send
- * @param cl a listener to notify on success/failure
- */
- public void sendCardsToPlayer (Client client, int playerIndex, Card[] cards,
- ConfirmListener cl);
-}
diff --git a/src/java/com/threerings/parlor/card/client/CardPanel.java b/src/java/com/threerings/parlor/card/client/CardPanel.java
index bef661b05..648a1263c 100644
--- a/src/java/com/threerings/parlor/card/client/CardPanel.java
+++ b/src/java/com/threerings/parlor/card/client/CardPanel.java
@@ -21,6 +21,7 @@
package com.threerings.parlor.card.client;
+import java.awt.Point;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
@@ -29,16 +30,19 @@ import java.util.Iterator;
import javax.swing.event.MouseInputAdapter;
import com.samskivert.util.ObserverList;
+import com.samskivert.util.QuickSort;
import com.threerings.media.FrameManager;
import com.threerings.media.VirtualMediaPanel;
-
import com.threerings.media.image.Mirage;
-
+import com.threerings.media.sprite.PathAdapter;
import com.threerings.media.sprite.Sprite;
+import com.threerings.media.util.LinePath;
+import com.threerings.media.util.Path;
+import com.threerings.media.util.PathSequence;
+import com.threerings.media.util.Pathable;
import com.threerings.parlor.card.Log;
-
import com.threerings.parlor.card.data.Card;
import com.threerings.parlor.card.data.CardCodes;
import com.threerings.parlor.card.data.Deck;
@@ -49,8 +53,947 @@ import com.threerings.parlor.card.data.Hand;
* and manipulating playing cards.
*/
public abstract class CardPanel extends VirtualMediaPanel
- implements CardCodes
+ implements CardCodes
{
+ /** The selection mode in which cards are not selectable. */
+ public static final int NONE = 0;
+
+ /** The selection mode in which the user can play a single card. */
+ public static final int PLAY_SINGLE = 1;
+
+ /** The selection mode in which the user can select a single card. */
+ public static final int SELECT_SINGLE = 2;
+
+ /** The selection mode in which the user can select multiple cards. */
+ public static final int SELECT_MULTIPLE = 3;
+
+ /**
+ * A predicate class for {@link CardSprite}s. Provides control over which
+ * cards are selectable, playable, etc.
+ */
+ public static interface CardSpritePredicate
+ {
+ /**
+ * Evaluates the specified sprite.
+ */
+ public boolean evaluate (CardSprite sprite);
+ }
+
+ /**
+ * A listener for card selection/deselection.
+ */
+ public static interface CardSelectionObserver
+ {
+ /**
+ * Called when a card has been played.
+ */
+ public void cardSpritePlayed (CardSprite sprite);
+
+ /**
+ * Called when a card has been selected.
+ */
+ public void cardSpriteSelected (CardSprite sprite);
+
+ /**
+ * Called when a card has been deselected.
+ */
+ public void cardSpriteDeselected (CardSprite sprite);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param frameManager the frame manager
+ */
+ public CardPanel (FrameManager frameManager)
+ {
+ super(frameManager);
+
+ // add a listener for mouse events
+ CardListener cl = new CardListener();
+ addMouseListener(cl);
+ addMouseMotionListener(cl);
+ }
+
+ /**
+ * Returns the full-sized image for the back of a playing card.
+ */
+ public abstract Mirage getCardBackImage ();
+
+ /**
+ * Returns the full-sized image for the front of the specified card.
+ */
+ public abstract Mirage getCardImage (Card card);
+
+ /**
+ * Returns the small-sized image for the back of a playing card.
+ */
+ public abstract Mirage getMicroCardBackImage ();
+
+ /**
+ * Returns the small-sized image for the front of the specified card.
+ */
+ public abstract Mirage getMicroCardImage (Card card);
+
+ /**
+ * Sets the location of the hand (the location of the center of the hand's
+ * upper edge).
+ */
+ public void setHandLocation (int x, int y)
+ {
+ _handLocation.setLocation(x, y);
+ }
+
+ /**
+ * Sets the horizontal spacing between cards in the hand.
+ */
+ public void setHandSpacing (int spacing)
+ {
+ _handSpacing = spacing;
+ }
+
+ /**
+ * Sets the vertical distance to offset cards that are selectable or
+ * playable.
+ */
+ public void setSelectableCardOffset (int offset)
+ {
+ _selectableCardOffset = offset;
+ }
+
+ /**
+ * Sets the vertical distance to offset cards that are selected.
+ */
+ public void setSelectedCardOffset (int offset)
+ {
+ _selectedCardOffset = offset;
+ }
+
+ /**
+ * Sets the selection mode for the hand (NONE, PLAY_SINGLE, SELECT_SINGLE,
+ * or SELECT_MULTIPLE). Changing the selection mode can change the
+ * current selection (clearing the selection, for example, if disabling
+ * selection mode).
+ */
+ public void setHandSelectionMode (int mode)
+ {
+ if (_handSelectionMode == mode) {
+ return;
+ }
+ _handSelectionMode = mode;
+
+ // if entered non-selection or single-selection mode, deselect all
+ // or all but one card
+ if (mode != SELECT_MULTIPLE) {
+ CardSprite[] sprites = getSelectedHandSprites();
+ int begin = (mode == SELECT_SINGLE ? 1 : 0);
+ for (int i = begin; i < sprites.length; i++) {
+ deselectHandSprite(sprites[i]);
+ }
+ }
+
+ // update the offsets of all cards in the hand
+ updateHandOffsets();
+ }
+
+ /**
+ * Sets the selection predicate that determines which cards from the hand
+ * may be selected (if null, all cards may be selected). Changing the
+ * predicate does not change the current selection.
+ */
+ public void setHandSelectionPredicate (CardSpritePredicate pred)
+ {
+ _handSelectionPredicate = pred;
+
+ // update the offsets of all cards in the hand
+ updateHandOffsets();
+ }
+
+ /**
+ * Returns the currently selected hand sprite (null if no sprites are
+ * selected, the first sprite if multiple sprites are selected).
+ */
+ public CardSprite getSelectedHandSprite ()
+ {
+ return _selectedHandSprites.size() == 0 ?
+ null : (CardSprite)_selectedHandSprites.get(0);
+ }
+
+ /**
+ * Returns an array containing the currently selected hand sprites
+ * (returns an empty array if no sprites are selected).
+ */
+ public CardSprite[] getSelectedHandSprites ()
+ {
+ return (CardSprite[])_selectedHandSprites.toArray(
+ new CardSprite[_selectedHandSprites.size()]);
+ }
+
+ /**
+ * Programmatically plays a sprite in the hand.
+ */
+ public void playHandSprite (final CardSprite sprite)
+ {
+ // notify the observers
+ ObserverList.ObserverOp op = new ObserverList.ObserverOp() {
+ public boolean apply (Object obs) {
+ ((CardSelectionObserver)obs).cardSpritePlayed(sprite);
+ return true;
+ }
+ };
+ _handSelectionObservers.apply(op);
+ }
+
+ /**
+ * Programmatically selects a sprite in the hand.
+ */
+ public void selectHandSprite (final CardSprite sprite)
+ {
+ // make sure it's not already selected
+ if (_selectedHandSprites.contains(sprite)) {
+ return;
+ }
+
+ // if in single card mode and there's another card selected,
+ // deselect it
+ if (_handSelectionMode == SELECT_SINGLE) {
+ CardSprite oldSprite = getSelectedHandSprite();
+ if (oldSprite != null) {
+ deselectHandSprite(oldSprite);
+ }
+ }
+
+ // add to list and update offset
+ _selectedHandSprites.add(sprite);
+ sprite.setLocation(sprite.getX(),
+ _handLocation.y - _selectedCardOffset);
+
+ // notify the observers
+ ObserverList.ObserverOp op = new ObserverList.ObserverOp() {
+ public boolean apply (Object obs) {
+ ((CardSelectionObserver)obs).cardSpriteSelected(sprite);
+ return true;
+ }
+ };
+ _handSelectionObservers.apply(op);
+ }
+
+ /**
+ * Programmatically deselects a sprite in the hand.
+ */
+ public void deselectHandSprite (final CardSprite sprite)
+ {
+ // make sure it's selected
+ if (!_selectedHandSprites.contains(sprite)) {
+ return;
+ }
+
+ // remove from list and update offset
+ _selectedHandSprites.remove(sprite);
+ sprite.setLocation(sprite.getX(), _handLocation.y);
+
+ // notify the observers
+ ObserverList.ObserverOp op = new ObserverList.ObserverOp() {
+ public boolean apply (Object obs) {
+ ((CardSelectionObserver)obs).cardSpriteDeselected(sprite);
+ return true;
+ }
+ };
+ _handSelectionObservers.apply(op);
+ }
+
+ /**
+ * Clears any existing hand sprite selection.
+ */
+ public void clearHandSelection ()
+ {
+ CardSprite[] sprites = getSelectedHandSprites();
+ for (int i = 0; i < sprites.length; i++) {
+ deselectHandSprite(sprites[i]);
+ }
+ }
+
+ /**
+ * Adds an object to the list of observers to notify when cards in the
+ * hand are selected/deselected.
+ */
+ public void addHandSelectionObserver (CardSelectionObserver obs)
+ {
+ _handSelectionObservers.add(obs);
+ }
+
+ /**
+ * Removes an object from the hand selection observer list.
+ */
+ public void removeHandSelectionObserver (CardSelectionObserver obs)
+ {
+ _handSelectionObservers.remove(obs);
+ }
+
+ /**
+ * Fades a hand of cards in.
+ *
+ * @param hand the hand of cards
+ * @param fadeDuration the amount of time to spend fading in
+ * the entire hand
+ */
+ public void setHand (Hand hand, long fadeDuration)
+ {
+ // make sure no cards are hanging around
+ clearHand();
+
+ // create the sprites
+ int size = hand.size();
+ for (int i = 0; i < size; i++) {
+ CardSprite cs = new CardSprite(this, (Card)hand.get(i));
+ _handSprites.add(cs);
+ }
+
+ // sort them
+ QuickSort.sort(_handSprites);
+
+ // fade them in at proper locations and layers
+ long cardDuration = fadeDuration / size;
+ for (int i = 0; i < size; i++) {
+ CardSprite cs = (CardSprite)_handSprites.get(i);
+ cs.setLocation(getHandX(size, i), _handLocation.y);
+ cs.setRenderOrder(i);
+ cs.addSpriteObserver(_handSpriteObserver);
+ addSprite(cs);
+ cs.fadeIn(i * cardDuration, cardDuration);
+ }
+ }
+
+ /**
+ * Fades a hand of cards in face-down.
+ *
+ * @param size the size of the hand
+ * @param fadeDuration the amount of time to spend fading in
+ * each card
+ */
+ public void setHand (int size, long fadeDuration)
+ {
+ // fill hand will null entries to signify unknown cards
+ Hand hand = new Hand();
+ for (int i = 0; i < size; i++) {
+ hand.add(null);
+ }
+ setHand(hand, fadeDuration);
+ }
+
+ /**
+ * Shows a hand that was previous set face-down.
+ *
+ * @param hand the hand of cards
+ */
+ public void showHand (Hand hand)
+ {
+ // sort the hand
+ QuickSort.sort(hand);
+
+ // set the sprites
+ int len = Math.min(_handSprites.size(), hand.size());
+ for (int i = 0; i < len; i++) {
+ CardSprite cs = (CardSprite)_handSprites.get(i);
+ cs.setCard((Card)hand.get(i));
+ }
+ }
+
+ /**
+ * Returns the first sprite in the hand that corresponds to the
+ * specified card, or null if the card is not in the hand.
+ */
+ public CardSprite getHandSprite (Card card)
+ {
+ return getCardSprite(_handSprites, card);
+ }
+
+ /**
+ * Clears all cards from the hand.
+ */
+ public void clearHand ()
+ {
+ clearHandSelection();
+ clearSprites(_handSprites);
+ }
+
+ /**
+ * Clears all cards from the board.
+ */
+ public void clearBoard ()
+ {
+ clearSprites(_boardSprites);
+ }
+
+ /**
+ * Flies a set of cards from the hand into the ether.
+ *
+ * @param cards the card sprites to remove from the hand
+ * @param dest the point to fly the cards to
+ * @param flightDuration the duration of the cards' flight
+ * @param fadePortion the amount of time to spend fading out
+ * as a proportion of the flight duration
+ */
+ public void flyFromHand (CardSprite[] cards, Point dest,
+ long flightDuration, float fadePortion)
+ {
+ // fly each sprite over, removing it from the hand immediately and
+ // from the board when it finishes its path
+ for (int i = 0; i < cards.length; i++) {
+ LinePath flight = new LinePath(dest, flightDuration);
+ cards[i].addSpriteObserver(_pathEndRemover);
+ cards[i].moveAndFadeOut(flight, flightDuration, fadePortion);
+ removeFromHand(cards[i]);
+ }
+
+ // adjust the hand to cover the hole
+ adjustHand(flightDuration, false);
+ }
+
+ /**
+ * Flies a set of cards from the ether into the hand.
+ *
+ * @param cards the cards to add to the hand
+ * @param src the point to fly the cards from
+ * @param flightDuration the duration of the cards' flight
+ * @param fadePortion the amount of time to spend fading in
+ * as a proportion of the flight duration
+ */
+ public void flyIntoHand (Card[] cards, Point src, long flightDuration,
+ float fadePortion)
+ {
+ // first create the sprites and add them to the list
+ CardSprite[] sprites = new CardSprite[cards.length];
+ for (int i = 0; i < cards.length; i++) {
+ sprites[i] = new CardSprite(this, cards[i]);
+ _handSprites.add(sprites[i]);
+ }
+
+ // settle the hand
+ adjustHand(flightDuration, true);
+
+ // then set the layers and fly the cards in
+ int size = _handSprites.size();
+ for (int i = 0; i < sprites.length; i++) {
+ int idx = _handSprites.indexOf(sprites[i]);
+ sprites[i].setLocation(src.x, src.y);
+ sprites[i].setRenderOrder(idx);
+ sprites[i].addSpriteObserver(_handSpriteObserver);
+ addSprite(sprites[i]);
+ LinePath flight = new LinePath(new Point(getHandX(size, idx),
+ _handLocation.y), flightDuration);
+ sprites[i].moveAndFadeIn(flight, flightDuration, fadePortion);
+ }
+ }
+
+ /**
+ * Flies a set of cards from the ether into the ether.
+ *
+ * @param cards the cards to fly across
+ * @param src the point to fly the cards from
+ * @param dest the point to fly the cards to
+ * @param flightDuration the duration of the cards' flight
+ * @param cardDelay the amount of time to wait between cards
+ * @param fadePortion the amount of time to spend fading in and out
+ * as a proportion of the flight duration
+ */
+ public void flyAcross (Card[] cards, Point src, Point dest,
+ long flightDuration, long cardDelay, float fadePortion)
+ {
+ for (int i = 0; i < cards.length; i++) {
+ // add on top of all board sprites
+ CardSprite cs = new CardSprite(this, cards[i]);
+ cs.setRenderOrder(getHighestBoardLayer() + 1 + i);
+ cs.setLocation(src.x, src.y);
+ addSprite(cs);
+
+ // prepend an initial delay to all cards after the first
+ Path path;
+ long pathDuration;
+ LinePath flight = new LinePath(dest, flightDuration);
+ if (i > 0) {
+ long delayDuration = cardDelay * i;
+ LinePath delay = new LinePath(src, delayDuration);
+ path = new PathSequence(delay, flight);
+ pathDuration = delayDuration + flightDuration;
+
+ } else {
+ path = flight;
+ pathDuration = flightDuration;
+ }
+ cs.addSpriteObserver(_pathEndRemover);
+ cs.moveAndFadeInAndOut(path, pathDuration, fadePortion);
+ }
+ }
+
+ /**
+ * Flies a set of cards from the ether into the ether face-down.
+ *
+ * @param number the number of cards to fly across
+ * @param src the point to fly the cards from
+ * @param dest the point to fly the cards to
+ * @param flightDuration the duration of the cards' flight
+ * @param cardDelay the amount of time to wait between cards
+ * @param fadePortion the amount of time to spend fading in and out
+ * as a proportion of the flight duration
+ */
+ public void flyAcross (int number, Point src, Point dest,
+ long flightDuration, long cardDelay, float fadePortion)
+ {
+ // use null values to signify unknown cards
+ flyAcross(new Card[number], src, dest, flightDuration,
+ cardDelay, fadePortion);
+ }
+
+ /**
+ * Flies a card from the hand onto the board.
+ *
+ * @param card the sprite to remove from the hand
+ * @param dest the point to fly the card to
+ * @param flightDuration the duration of the card's flight
+ */
+ public void flyFromHandToBoard (CardSprite card, Point dest,
+ long flightDuration)
+ {
+ // fly it over
+ LinePath flight = new LinePath(dest, flightDuration);
+ card.move(flight);
+
+ // lower the board so that the card from hand is on top
+ lowerBoardSprites(card.getRenderOrder() - 1);
+
+ // move from one list to the other
+ removeFromHand(card);
+ _boardSprites.add(card);
+
+ // adjust the hand to cover the hole
+ adjustHand(flightDuration, false);
+ }
+
+ /**
+ * Flies a card from the ether onto the board.
+ *
+ * @param card the card to add to the board
+ * @param src the point to fly the card from
+ * @param dest the point to fly the card to
+ * @param flightDuration the duration of the card's flight
+ * @param fadePortion the amount of time to spend fading in as a
+ * proportion of the flight duration
+ */
+ public void flyToBoard (Card card, Point src, Point dest,
+ long flightDuration, float fadePortion)
+ {
+ // add it on top of the existing cards
+ CardSprite cs = new CardSprite(this, card);
+ cs.setRenderOrder(getHighestBoardLayer() + 1);
+ cs.setLocation(src.x, src.y);
+ addSprite(cs);
+ _boardSprites.add(cs);
+
+ // and fly it over
+ LinePath flight = new LinePath(dest, flightDuration);
+ cs.moveAndFadeIn(flight, flightDuration, fadePortion);
+ }
+
+ /**
+ * Adds a card to the board immediately.
+ *
+ * @param card the card to add to the board
+ * @param dest the point at which to add the card
+ */
+ public void addToBoard (Card card, Point dest)
+ {
+ CardSprite cs = new CardSprite(this, card);
+ cs.setRenderOrder(getHighestBoardLayer() + 1);
+ cs.setLocation(dest.x, dest.y);
+ addSprite(cs);
+ _boardSprites.add(cs);
+ }
+
+ /**
+ * Flies a set of cards from the board into the ether.
+ *
+ * @param cards the cards to remove from the board
+ * @param dest the point to fly the cards to
+ * @param flightDuration the duration of the cards' flight
+ * @param fadePortion the amount of time to spend fading out as a
+ * proportion of the flight duration
+ */
+ public void flyFromBoard (CardSprite[] cards, Point dest,
+ long flightDuration, float fadePortion)
+ {
+ for (int i = 0; i < cards.length; i++) {
+ LinePath flight = new LinePath(dest, flightDuration);
+ cards[i].addSpriteObserver(_pathEndRemover);
+ cards[i].moveAndFadeOut(flight, flightDuration, fadePortion);
+ _boardSprites.remove(cards[i]);
+ }
+ }
+
+ /**
+ * Flies a set of cards from the board into the ether through an
+ * intermediate point.
+ *
+ * @param cards the cards to remove from the board
+ * @param dest1 the first point to fly the cards to
+ * @param dest2 the final destination of the cards
+ * @param flightDuration the duration of the cards' flight
+ * @param fadePortion the amount of time to spend fading out as a
+ * proportion of the flight duration
+ */
+ public void flyFromBoard (CardSprite[] cards, Point dest1, Point dest2,
+ long flightDuration, float fadePortion)
+ {
+ for (int i = 0; i < cards.length; i++) {
+ PathSequence flight = new PathSequence(
+ new LinePath(dest1, flightDuration/2),
+ new LinePath(dest1, dest2, flightDuration/2));
+ cards[i].addSpriteObserver(_pathEndRemover);
+ cards[i].moveAndFadeOut(flight, flightDuration, fadePortion);
+ _boardSprites.remove(cards[i]);
+ }
+ }
+
+ /**
+ * Returns the first card sprite in the specified list that represents
+ * the specified card, or null if there is no such sprite in the list.
+ */
+ protected CardSprite getCardSprite (ArrayList list, Card card)
+ {
+ for (int i = 0; i < list.size(); i++) {
+ CardSprite cs = (CardSprite)list.get(i);
+ if (card.equals(cs.getCard())) {
+ return cs;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Expands or collapses the hand to accommodate new cards or cover the
+ * space left by removed cards. Skips unmanaged sprites.
+ *
+ * @param adjustDuration the amount of time to spend settling the cards
+ * into their new locations
+ * @param updateLayers whether or not to update the layers of the cards
+ */
+ protected void adjustHand (long adjustDuration, boolean updateLayers)
+ {
+ // Sort the hand
+ QuickSort.sort(_handSprites);
+
+ // Move each card to its proper position (and, optionally, layer)
+ int size = _handSprites.size();
+ for (int i = 0; i < size; i++) {
+ CardSprite cs = (CardSprite)_handSprites.get(i);
+ if (!isManaged(cs)) {
+ continue;
+ }
+ if (updateLayers) {
+ removeSprite(cs);
+ cs.setRenderOrder(i);
+ addSprite(cs);
+ }
+ LinePath adjust = new LinePath(new Point(getHandX(size, i),
+ getHandY(cs, false)), adjustDuration);
+ cs.move(adjust);
+ }
+ }
+
+ /**
+ * Removes a card from the hand, deselecting it if selected.
+ */
+ protected void removeFromHand (CardSprite card)
+ {
+ if (_selectedHandSprites.contains(card)) {
+ deselectHandSprite(card);
+ }
+ _handSprites.remove(card);
+ }
+
+ /**
+ * Updates the offsets of all the cards in the hand. If there is only
+ * one selectable card, that card will always be raised slightly.
+ */
+ protected void updateHandOffsets ()
+ {
+ int size = _handSprites.size();
+ for (int i = 0; i < size; i++) {
+ CardSprite cs = (CardSprite)_handSprites.get(i);
+ cs.setLocation(cs.getX(), getHandY(cs, false));
+ }
+ }
+
+ /**
+ * Given the location and spacing of the hand, returns the x location of
+ * the card at the specified index within a hand of the specified size.
+ */
+ protected int getHandX (int size, int idx)
+ {
+ // get the card width from the image if not yet known
+ if (_cardWidth == 0) {
+ _cardWidth = getCardBackImage().getWidth();
+ }
+ // first compute the width of the entire hand, then use that to
+ // determine the centered location
+ int width = (size - 1) * _handSpacing + _cardWidth;
+ return (_handLocation.x - width/2) + idx * _handSpacing;
+ }
+
+ /**
+ * Determines the y location of the specified card, given its selection
+ * state.
+ *
+ * @param mouseOver whether or not the mouse cursor is currently on the
+ * card
+ */
+ protected int getHandY (CardSprite card, boolean mouseOver)
+ {
+ if (_selectedHandSprites.contains(card)) {
+ return _handLocation.y - _selectedCardOffset;
+
+ } else if (isSelectable(card) && (mouseOver ||
+ getSelectableCount() == 1)) {
+ return _handLocation.y - _selectableCardOffset;
+
+ } else {
+ return _handLocation.y;
+ }
+ }
+
+ /**
+ * Returns the number of sprites in the hand that are selectable.
+ */
+ protected int getSelectableCount ()
+ {
+ int size = _handSprites.size(), count = 0;
+ for (int i = 0; i < size; i++) {
+ if (isSelectable((CardSprite)_handSprites.get(i))) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Given the current selection mode and predicate, determines if the
+ * specified sprite is selectable.
+ */
+ protected boolean isSelectable (CardSprite sprite) {
+ return _handSelectionMode != NONE &&
+ (_handSelectionPredicate == null ||
+ _handSelectionPredicate.evaluate(sprite));
+ }
+
+ /**
+ * Lowers all board sprites so that they are rendered at or below the
+ * specified layer.
+ */
+ protected void lowerBoardSprites (int layer)
+ {
+ // see if they're already lower
+ int highest = getHighestBoardLayer();
+ if (highest <= layer) {
+ return;
+ }
+
+ // lower them just enough
+ int size = _boardSprites.size(), adjustment = layer - highest;
+ for (int i = 0; i < size; i++) {
+ CardSprite cs = (CardSprite)_boardSprites.get(i);
+ removeSprite(cs);
+ cs.setRenderOrder(cs.getRenderOrder() + adjustment);
+ addSprite(cs);
+ }
+ }
+
+ /**
+ * Returns the highest render order of any sprite on the board.
+ */
+ protected int getHighestBoardLayer ()
+ {
+ // must be at least zero, because that's the lowest number we can push
+ // the sprites down to (the layer of the first card in the hand)
+ int size = _boardSprites.size(), highest = 0;
+ for (int i = 0; i < size; i++) {
+ highest = Math.max(highest,
+ ((CardSprite)_boardSprites.get(i)).getRenderOrder());
+ }
+ return highest;
+ }
+
+ /**
+ * Clears an array of sprites from the specified list and from the panel.
+ */
+ protected void clearSprites (ArrayList sprites)
+ {
+ for (Iterator it = sprites.iterator(); it.hasNext(); ) {
+ removeSprite((CardSprite)it.next());
+ it.remove();
+ }
+ }
+
+ /** The width of the playing cards. */
+ protected int _cardWidth;
+
+ /** The sprites for cards within the hand. */
+ protected ArrayList _handSprites = new ArrayList();
+
+ /** The sprites for cards within the hand that have been selected. */
+ protected ArrayList _selectedHandSprites = new ArrayList();
+
+ /** The sprites for cards within the hand that are selectable. */
+ protected ArrayList _selectableHandSprites = new ArrayList();
+
+ /** The current selection mode for the hand. */
+ protected int _handSelectionMode;
+
+ /** The predicate that determines which cards are selectable (if null, all
+ * cards are selectable). */
+ protected CardSpritePredicate _handSelectionPredicate;
+
+ /** Observers of hand card selection/deselection. */
+ protected ObserverList _handSelectionObservers = new ObserverList(
+ ObserverList.FAST_UNSAFE_NOTIFY);
+
+ /** The location of the center of the hand's upper edge. */
+ protected Point _handLocation = new Point();
+
+ /** The horizontal distance between cards in the hand. */
+ protected int _handSpacing;
+
+ /** The vertical distance to offset cards that are selectable. */
+ protected int _selectableCardOffset;
+
+ /** The vertical distance to offset cards that are selected. */
+ protected int _selectedCardOffset;
+
+ /** The sprites for cards on the board. */
+ protected ArrayList _boardSprites = new ArrayList();
+
+ /** A path observer that removes the sprite at the end of its path. */
+ protected PathAdapter _pathEndRemover = new PathAdapter() {
+ public void pathCompleted (Sprite sprite, Path path, long when) {
+ removeSprite(sprite);
+ }
+ };
+
+ /** Listens for interactions with cards in hand. */
+ protected CardSpriteObserver _handSpriteObserver =
+ new CardSpriteObserver() {
+ public void cardSpriteClicked (CardSprite sprite, MouseEvent me) {
+ // select, deselect, or play card in hand
+ if (_selectedHandSprites.contains(sprite)) {
+ deselectHandSprite(sprite);
+
+ } else if (_handSprites.contains(sprite) && isSelectable(sprite)) {
+ if (_handSelectionMode == PLAY_SINGLE) {
+ playHandSprite(sprite);
+
+ } else {
+ selectHandSprite(sprite);
+ }
+ }
+ }
+
+ public void cardSpriteEntered (CardSprite sprite, MouseEvent me) {
+ // update the offset
+ if (_handSprites.contains(sprite)) {
+ sprite.setLocation(sprite.getX(), getHandY(sprite, true));
+ }
+ }
+
+ public void cardSpriteExited (CardSprite sprite, MouseEvent me) {
+ // update the offset
+ if (_handSprites.contains(sprite)) {
+ sprite.setLocation(sprite.getX(), getHandY(sprite, false));
+ }
+ }
+
+ public void cardSpriteDragged (CardSprite sprite, MouseEvent me) {
+ }
+ };
+
+ /** Listens for mouse interactions with cards. */
+ protected class CardListener extends MouseInputAdapter
+ {
+ public void mousePressed (MouseEvent me)
+ {
+ if (_activeCardSprite != null &&
+ isManaged(_activeCardSprite)) {
+ _handleX = _activeCardSprite.getX() - me.getX();
+ _handleY = _activeCardSprite.getY() - me.getY();
+ _hasBeenDragged = false;
+ }
+ }
+
+ public void mouseReleased (MouseEvent me)
+ {
+ if (_activeCardSprite != null &&
+ isManaged(_activeCardSprite) &&
+ _hasBeenDragged) {
+ _activeCardSprite.queueNotification(
+ new CardSpriteDraggedOp(_activeCardSprite, me)
+ );
+ }
+ }
+
+ public void mouseClicked (MouseEvent me)
+ {
+ if (_activeCardSprite != null &&
+ isManaged(_activeCardSprite)) {
+ _activeCardSprite.queueNotification(
+ new CardSpriteClickedOp(_activeCardSprite, me)
+ );
+ }
+ }
+
+ public void mouseMoved (MouseEvent me)
+ {
+ Sprite newHighestHit = _spritemgr.getHighestHitSprite(
+ me.getX(), me.getY());
+
+ CardSprite newActiveCardSprite =
+ (newHighestHit instanceof CardSprite ?
+ (CardSprite)newHighestHit : null);
+
+ if (_activeCardSprite != newActiveCardSprite) {
+ if (_activeCardSprite != null &&
+ isManaged(_activeCardSprite)) {
+ _activeCardSprite.queueNotification(
+ new CardSpriteExitedOp(_activeCardSprite, me)
+ );
+ }
+ _activeCardSprite = newActiveCardSprite;
+ if (_activeCardSprite != null) {
+ _activeCardSprite.queueNotification(
+ new CardSpriteEnteredOp(_activeCardSprite, me)
+ );
+ }
+ }
+ }
+
+ public void mouseDragged (MouseEvent me)
+ {
+ if (_activeCardSprite != null &&
+ isManaged(_activeCardSprite) &&
+ _activeCardSprite.isDraggable()) {
+ _activeCardSprite.setLocation(
+ me.getX() + _handleX,
+ me.getY() + _handleY
+ );
+ _hasBeenDragged = true;
+
+ } else {
+ mouseMoved(me);
+ }
+ }
+
+ protected CardSprite _activeCardSprite;
+ protected int _handleX, _handleY;
+ protected boolean _hasBeenDragged;
+ }
+
/** Calls CardSpriteObserver.cardSpriteClicked. */
protected static class CardSpriteClickedOp implements
ObserverList.ObserverOp
@@ -63,8 +1006,10 @@ public abstract class CardPanel extends VirtualMediaPanel
public boolean apply (Object observer)
{
- ((CardSpriteObserver)observer).cardSpriteClicked(_sprite,
- _me);
+ if (observer instanceof CardSpriteObserver) {
+ ((CardSpriteObserver)observer).cardSpriteClicked(_sprite,
+ _me);
+ }
return true;
}
@@ -84,7 +1029,10 @@ public abstract class CardPanel extends VirtualMediaPanel
public boolean apply (Object observer)
{
- ((CardSpriteObserver)observer).cardSpriteEntered(_sprite, _me);
+ if (observer instanceof CardSpriteObserver) {
+ ((CardSpriteObserver)observer).cardSpriteEntered(_sprite,
+ _me);
+ }
return true;
}
@@ -104,7 +1052,9 @@ public abstract class CardPanel extends VirtualMediaPanel
public boolean apply (Object observer)
{
- ((CardSpriteObserver)observer).cardSpriteExited(_sprite, _me);
+ if (observer instanceof CardSpriteObserver) {
+ ((CardSpriteObserver)observer).cardSpriteExited(_sprite, _me);
+ }
return true;
}
@@ -124,114 +1074,14 @@ public abstract class CardPanel extends VirtualMediaPanel
public boolean apply (Object observer)
{
- ((CardSpriteObserver)observer).cardSpriteDragged(_sprite, _me);
+ if (observer instanceof CardSpriteObserver) {
+ ((CardSpriteObserver)observer).cardSpriteDragged(_sprite,
+ _me);
+ }
return true;
}
protected CardSprite _sprite;
protected MouseEvent _me;
}
-
- /**
- * Constructor.
- *
- * @param frameManager the frame manager
- */
- public CardPanel (FrameManager frameManager)
- {
- super(frameManager);
-
- MouseInputAdapter mia = new MouseInputAdapter() {
- public void mousePressed (MouseEvent me) {
- if (_activeCardSprite != null &&
- isManaged(_activeCardSprite)) {
- _handleX = _activeCardSprite.getX() - me.getX();
- _handleY = _activeCardSprite.getY() - me.getY();
- _hasBeenDragged = false;
- }
- }
- public void mouseReleased (MouseEvent me) {
- if (_activeCardSprite != null &&
- isManaged(_activeCardSprite) &&
- _hasBeenDragged) {
- _activeCardSprite.queueNotification(
- new CardSpriteDraggedOp(_activeCardSprite, me)
- );
- }
- }
- public void mouseClicked (MouseEvent me) {
- if (_activeCardSprite != null &&
- isManaged(_activeCardSprite)) {
- _activeCardSprite.queueNotification(
- new CardSpriteClickedOp(_activeCardSprite, me)
- );
- }
- }
- public void mouseMoved (MouseEvent me)
- {
- Sprite newHighestHit = _spritemgr.getHighestHitSprite(
- me.getX(), me.getY());
-
- CardSprite newActiveCardSprite =
- (newHighestHit instanceof CardSprite ?
- (CardSprite)newHighestHit : null);
-
- if (_activeCardSprite != newActiveCardSprite) {
- if (_activeCardSprite != null &&
- isManaged(_activeCardSprite)) {
- _activeCardSprite.queueNotification(
- new CardSpriteExitedOp(_activeCardSprite, me)
- );
- }
- _activeCardSprite = newActiveCardSprite;
- if (_activeCardSprite != null) {
- _activeCardSprite.queueNotification(
- new CardSpriteEnteredOp(_activeCardSprite, me)
- );
- }
- }
- }
- public void mouseDragged (MouseEvent me)
- {
- if (_activeCardSprite != null &&
- isManaged(_activeCardSprite) &&
- _activeCardSprite.isDraggable()) {
- _activeCardSprite.setLocation(
- me.getX() + _handleX,
- me.getY() + _handleY
- );
- _hasBeenDragged = true;
- } else {
- mouseMoved(me);
- }
- }
- };
-
- addMouseListener(mia);
- addMouseMotionListener(mia);
- }
-
- /**
- * Returns the image for the back of a playing card.
- *
- * @return the card back image
- */
- public abstract Mirage getCardBackImage ();
-
- /**
- * Returns the image for the front of the specified card.
- *
- * @param card the desired card
- * @return the card front image
- */
- public abstract Mirage getCardImage (Card card);
-
- /** The active card sprite. */
- protected CardSprite _activeCardSprite;
-
- /** The location of the cursor in the active sprite. */
- protected int _handleX, _handleY;
-
- /** Whether or not the active sprite has been dragged. */
- protected boolean _hasBeenDragged;
}
diff --git a/src/java/com/threerings/parlor/card/client/CardSprite.java b/src/java/com/threerings/parlor/card/client/CardSprite.java
index fcb8864c4..8ff0a7a0b 100644
--- a/src/java/com/threerings/parlor/card/client/CardSprite.java
+++ b/src/java/com/threerings/parlor/card/client/CardSprite.java
@@ -21,9 +21,13 @@
package com.threerings.parlor.card.client;
-import com.threerings.media.image.Mirage;
+import java.awt.AlphaComposite;
+import java.awt.Composite;
+import java.awt.Graphics2D;
+import com.threerings.media.image.Mirage;
import com.threerings.media.sprite.OrientableImageSprite;
+import com.threerings.media.util.Path;
import com.threerings.parlor.card.data.Card;
@@ -31,12 +35,14 @@ import com.threerings.parlor.card.data.Card;
* A sprite representing a playing card.
*/
public class CardSprite extends OrientableImageSprite
+ implements Comparable
{
/**
* Creates a new upward-facing card sprite.
*
* @param panel the panel responsible for the sprite
- * @param card the card to depict
+ * @param card the card to depict (can be null, in which case the
+ * card back will be shown)
*/
public CardSprite (CardPanel panel, Card card)
{
@@ -127,13 +133,199 @@ public class CardSprite extends OrientableImageSprite
return _draggable;
}
+ /**
+ * Sets the alpha value of this sprite.
+ */
+ public void setAlpha (float alpha)
+ {
+ if (alpha < 0.0f) {
+ alpha = 0.0f;
+
+ } else if (alpha > 1.0f) {
+ alpha = 1.0f;
+ }
+ if (alpha != _alphaComposite.getAlpha()) {
+ _alphaComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
+ alpha);
+ if (_mgr != null) {
+ _mgr.getRegionManager().invalidateRegion(_bounds);
+ }
+ }
+ }
+
+ /**
+ * Returns the alpha value of this sprite.
+ */
+ public float getAlpha ()
+ {
+ return _alphaComposite.getAlpha();
+ }
+
+ /**
+ * Fades this sprite in over the specified duration after
+ * waiting for the specified delay.
+ */
+ public void fadeIn (long delay, long duration)
+ {
+ setAlpha(0.0f);
+
+ _fadeStamp = 0;
+ _fadeDelay = delay;
+ _fadeInDuration = duration;
+ }
+
+ /**
+ * Puts this sprite on the specified path and fades it in over
+ * the specified duration.
+ *
+ * @param path the path to move along
+ * @param fadePortion the portion of time to spend fading in, from 0.0f (no time)
+ * to 1.0f (the entire time)
+ */
+ public void moveAndFadeIn (Path path, long pathDuration, float fadePortion)
+ {
+ move(path);
+
+ setAlpha(0.0f);
+
+ _fadeInDuration = (long)(pathDuration*fadePortion);
+ }
+
+ /**
+ * Puts this sprite on the specified path and fades it out over
+ * the specified duration.
+ *
+ * @param path the path to move along
+ * @param pathDuration the duration of the path
+ * @param fadePortion the portion of time to spend fading out, from 0.0f (no time)
+ * to 1.0f (the entire time)
+ */
+ public void moveAndFadeOut (Path path, long pathDuration, float fadePortion)
+ {
+ move(path);
+
+ setAlpha(1.0f);
+
+ _pathDuration = pathDuration;
+ _fadeOutDuration = (long)(pathDuration*fadePortion);
+ }
+
+ /**
+ * Puts this sprite on the specified path, fading it in over the specified
+ * duration at the beginning and fading it out at the end.
+ *
+ * @param path the path to move along
+ * @param pathDuration the duration of the path
+ * @param fadePortion the portion of time to spend fading in/out, from
+ * 0.0f (no time) to 1.0f (the entire time)
+ */
+ public void moveAndFadeInAndOut (Path path, long pathDuration,
+ float fadePortion)
+ {
+ move(path);
+
+ setAlpha(0.0f);
+
+ _pathDuration = pathDuration;
+ _fadeInDuration = _fadeOutDuration = (long)(pathDuration*fadePortion);
+ }
+
+ // Documentation inherited.
+ public void tick (long tickStamp)
+ {
+ super.tick(tickStamp);
+
+ if (_fadeInDuration != -1) {
+ if (_path != null && (tickStamp-_pathStamp) <= _fadeInDuration) {
+ // fading in while moving
+ float alpha = (float)(tickStamp-_pathStamp)/_fadeInDuration;
+ if (alpha >= 1.0f) {
+ // fade-in complete
+ setAlpha(1.0f);
+ _fadeInDuration = -1;
+
+ } else {
+ setAlpha(alpha);
+ }
+
+ } else {
+ // fading in while stationary
+ if (_fadeStamp == 0) {
+ // store the time at which fade started
+ _fadeStamp = tickStamp;
+ }
+ if (tickStamp > _fadeStamp + _fadeDelay) {
+ // initial delay has passed
+ float alpha = (float)(tickStamp-_fadeStamp-_fadeDelay)/_fadeInDuration;
+ if (alpha >= 1.0f) {
+ // fade-in complete
+ setAlpha(1.0f);
+ _fadeInDuration = -1;
+
+ } else {
+ setAlpha(alpha);
+ }
+ }
+ }
+
+ } else if(_fadeOutDuration != -1 && _pathStamp+_pathDuration-tickStamp
+ <= _fadeOutDuration) {
+ // fading out while moving
+ float alpha = (float)(_pathStamp+_pathDuration-tickStamp)/_fadeOutDuration;
+ setAlpha(alpha);
+ }
+ }
+
+ // Documentation inherited.
+ public void pathCompleted (long timestamp)
+ {
+ super.pathCompleted(timestamp);
+
+ if (_fadeInDuration != -1) {
+ setAlpha(1.0f);
+ _fadeInDuration = -1;
+
+ } else if(_fadeOutDuration != -1) {
+ setAlpha(0.0f);
+ _fadeOutDuration = -1;
+ }
+ }
+
+ // Documentation inherited.
+ public void paint (Graphics2D gfx)
+ {
+ if (_alphaComposite.getAlpha() < 1.0f) {
+ Composite ocomp = gfx.getComposite();
+ gfx.setComposite(_alphaComposite);
+ super.paint(gfx);
+ gfx.setComposite(ocomp);
+
+ } else {
+ super.paint(gfx);
+ }
+ }
+
+ /**
+ * Compares this to another card sprite based on their cards.
+ */
+ public int compareTo (Object other)
+ {
+ CardSprite cs = (CardSprite)other;
+ if (_card == null || cs._card == null) {
+ return 0;
+
+ } else {
+ return _card.compareTo(cs._card);
+ }
+ }
+
/**
* Updates the mirage according to the current state.
*/
protected void updateMirage ()
{
- setMirage(_facingUp ? _panel.getCardImage(_card) :
- _panel.getCardBackImage());
+ setMirage((_card != null && _facingUp ) ?
+ _panel.getCardImage(_card) : _panel.getCardBackImage());
}
/** The panel responsible for the sprite. */
@@ -147,4 +339,23 @@ public class CardSprite extends OrientableImageSprite
/** Whether or not the user can drag the card around the board. */
protected boolean _draggable;
+
+ /** The alpha composite. */
+ protected AlphaComposite _alphaComposite =
+ AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f);
+
+ /** If fading in, the fade-in duration (otherwise -1). */
+ protected long _fadeInDuration = -1;
+
+ /** If fading in without moving, the fade-in delay. */
+ protected long _fadeDelay;
+
+ /** The time at which fading started. */
+ protected long _fadeStamp = -1;
+
+ /** If fading out, the fade-out duration (otherwise -1). */
+ protected long _fadeOutDuration = -1;
+
+ /** If fading out, the path duration. */
+ protected long _pathDuration;
}
diff --git a/src/java/com/threerings/parlor/card/data/CardGameObject.java b/src/java/com/threerings/parlor/card/data/CardGameObject.java
index 6ef05010f..14cd594f6 100644
--- a/src/java/com/threerings/parlor/card/data/CardGameObject.java
+++ b/src/java/com/threerings/parlor/card/data/CardGameObject.java
@@ -28,29 +28,4 @@ import com.threerings.parlor.game.data.GameObject;
*/
public class CardGameObject extends GameObject
{
- // AUTO-GENERATED: FIELDS START
- /** The field name of the cardGameService field. */
- public static final String CARD_GAME_SERVICE = "cardGameService";
- // AUTO-GENERATED: FIELDS END
-
- /** The card game service interface. */
- public CardGameMarshaller cardGameService;
-
- // AUTO-GENERATED: METHODS START
- /**
- * Requests that the cardGameService field be set to the
- * specified value. The local value will be updated immediately and an
- * event will be propagated through the system to notify all listeners
- * that the attribute did change. Proxied copies of this object (on
- * clients) will apply the value change when they received the
- * attribute changed notification.
- */
- public void setCardGameService (CardGameMarshaller value)
- {
- CardGameMarshaller ovalue = this.cardGameService;
- requestAttributeChange(
- CARD_GAME_SERVICE, value, ovalue);
- this.cardGameService = value;
- }
- // AUTO-GENERATED: METHODS END
}
diff --git a/src/java/com/threerings/parlor/card/data/Deck.java b/src/java/com/threerings/parlor/card/data/Deck.java
index 351533b66..9f8613780 100644
--- a/src/java/com/threerings/parlor/card/data/Deck.java
+++ b/src/java/com/threerings/parlor/card/data/Deck.java
@@ -23,28 +23,20 @@ package com.threerings.parlor.card.data;
import java.util.Collections;
-import com.threerings.io.Streamable;
-
import com.threerings.util.StreamableArrayList;
/**
* Instances of this class represent decks of cards.
*/
-public class Deck implements CardCodes,
- Streamable
+public class Deck extends StreamableArrayList
+ implements CardCodes
{
- /** The cards in the deck. */
- public StreamableArrayList cards;
-
-
/**
* Default constructor creates an unshuffled deck of cards without
* jokers.
*/
public Deck ()
{
- cards = new StreamableArrayList();
-
reset(false);
}
@@ -56,8 +48,6 @@ public class Deck implements CardCodes,
*/
public Deck (boolean includeJokers)
{
- cards = new StreamableArrayList();
-
reset(includeJokers);
}
@@ -70,17 +60,17 @@ public class Deck implements CardCodes,
*/
public void reset (boolean includeJokers)
{
- cards.clear();
+ clear();
- for (int i=SPADES;i<=DIAMONDS;i++) {
- for (int j=2;j<=ACE;j++) {
- cards.add(new Card(j, i));
+ for (int i = SPADES; i <= DIAMONDS; i++) {
+ for (int j = 2; j <= ACE; j++) {
+ add(new Card(j, i));
}
}
if (includeJokers) {
- cards.add(new Card(RED_JOKER, 3));
- cards.add(new Card(BLACK_JOKER, 3));
+ add(new Card(RED_JOKER, 3));
+ add(new Card(BLACK_JOKER, 3));
}
}
@@ -89,7 +79,7 @@ public class Deck implements CardCodes,
*/
public void shuffle ()
{
- Collections.shuffle(cards);
+ Collections.shuffle(this);
}
/**
@@ -101,15 +91,15 @@ public class Deck implements CardCodes,
*/
public Hand dealHand (int size)
{
- if (cards.size() < size) {
+ if (size() < size) {
return null;
- }
- else {
+
+ } else {
Hand hand = new Hand();
- int cardsLeft = cards.size();
- for (int i=0;i 0;
+ }
+
+ /**
+ * Determines the number of cards that belong to the specified suit within
+ * the array given.
+ */
+ public static int countSuitMembers (PlayerCard[] cards, int suit)
+ {
+ int count = 0;
+ for (int i = 0; i < cards.length; i++) {
+ if (cards[i].card.getSuit() == suit) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Checks whether the proposed card follows the suit lead.
+ */
+ public static boolean followsSuit (PlayerCard[] cardsPlayed, Card card)
+ {
+ return cardsPlayed[0].card.getSuit() == card.getSuit();
+ }
+
+ /**
+ * Returns the highest card (according to the standard A,K,...,2 ordering)
+ * in the suit lead, with an optional trump suit.
+ *
+ * @param trumpSuit the trump suit, or -1 for none
+ */
+ public static PlayerCard getHighestInLeadSuit (PlayerCard[] cardsPlayed,
+ int trumpSuit)
+ {
+ PlayerCard highest = cardsPlayed[0];
+ for (int i = 1; i < cardsPlayed.length; i++) {
+ PlayerCard other = cardsPlayed[i];
+ if ((other.card.getSuit() == highest.card.getSuit() &&
+ other.card.compareTo(highest.card) > 0) ||
+ (other.card.getSuit() == trumpSuit &&
+ highest.card.getSuit() != trumpSuit)) {
+ highest = other;
+ }
+ }
+ return highest;
+ }
+
+ /** The locations of the other players for each player index. */
+ protected static final int[][] RELATIVE_LOCATIONS = {
+ {BOTTOM, TOP, RIGHT, LEFT}, {TOP, BOTTOM, LEFT, RIGHT},
+ {LEFT, RIGHT, BOTTOM, TOP}, {RIGHT, LEFT, TOP, BOTTOM} };
+}