Holy crap Batman! It's the beginnings of refactoring the basic puzzle
stuff into Narya. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@2876 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// $Id: PlayerStatusView.java,v 1.1 2003/11/26 01:42:34 mdb Exp $
|
||||
|
||||
package com.threerings.puzzle.client;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import com.threerings.parlor.game.GameObject;
|
||||
|
||||
import com.threerings.puzzle.Log;
|
||||
import com.threerings.puzzle.data.BoardSummary;
|
||||
import com.threerings.puzzle.data.PuzzleConfig;
|
||||
import com.threerings.puzzle.data.PuzzleObject;
|
||||
|
||||
/**
|
||||
* The player status view displays a player's current status in the game.
|
||||
*/
|
||||
public class PlayerStatusView extends JPanel
|
||||
{
|
||||
/**
|
||||
* Constructs a player status view.
|
||||
*/
|
||||
public PlayerStatusView (GameObject gameobj, int pidx)
|
||||
{
|
||||
// save off references
|
||||
_gameobj = gameobj;
|
||||
_username = _gameobj.players[pidx];
|
||||
_pidx = pidx;
|
||||
|
||||
// configure the panel
|
||||
setOpaque(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the player status view with the puzzle config.
|
||||
*/
|
||||
public void init (PuzzleConfig config)
|
||||
{
|
||||
// nothing for now
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the player index of the player represented by this view.
|
||||
*/
|
||||
public int getPlayerIndex ()
|
||||
{
|
||||
return _pidx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the player board summary.
|
||||
*/
|
||||
public void setBoardSummary (BoardSummary summary)
|
||||
{
|
||||
_summary = summary;
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the player status.
|
||||
*/
|
||||
public void setStatus (int status)
|
||||
{
|
||||
_status = status;
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to highlight the player status display when rendered.
|
||||
*/
|
||||
public void setHighlighted (boolean highlight)
|
||||
{
|
||||
_highlight = highlight;
|
||||
repaint();
|
||||
}
|
||||
|
||||
/** Returns a string representation of this instance. */
|
||||
public String toString ()
|
||||
{
|
||||
return "[user=" + _username + ", pidx=" + _pidx +
|
||||
", status=" + _status + "]";
|
||||
}
|
||||
|
||||
/** The game object associated with this view. */
|
||||
protected GameObject _gameobj;
|
||||
|
||||
/** The player name. */
|
||||
protected String _username;
|
||||
|
||||
/** The player index. */
|
||||
protected int _pidx;
|
||||
|
||||
/** Whether this display is highlighted. */
|
||||
protected boolean _highlight;
|
||||
|
||||
/** The player board summary. */
|
||||
protected BoardSummary _summary;
|
||||
|
||||
/** The player game status. */
|
||||
protected int _status = PuzzleObject.PLAYER_IN_PLAY;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// $Id: PuzzleAnimationWaiter.java,v 1.1 2003/11/26 01:42:34 mdb Exp $
|
||||
|
||||
package com.threerings.puzzle.client;
|
||||
|
||||
import com.threerings.media.animation.AnimationWaiter;
|
||||
|
||||
import com.threerings.puzzle.Log;
|
||||
import com.threerings.puzzle.data.PuzzleObject;
|
||||
|
||||
/**
|
||||
* An animation waiter to be used with puzzles that want to modify the
|
||||
* game object or board in some way after the animations end, and would
|
||||
* like to do so in a safe fashion such that their changes aren't
|
||||
* unwittingly performed on game data for a subsequent round of the
|
||||
* puzzle.
|
||||
*/
|
||||
public abstract class PuzzleAnimationWaiter extends AnimationWaiter
|
||||
{
|
||||
/**
|
||||
* Constructs a puzzle animation waiter.
|
||||
*/
|
||||
public PuzzleAnimationWaiter (PuzzleObject puzobj)
|
||||
{
|
||||
_puzobj = puzobj;
|
||||
_roundId = puzobj.roundId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the puzzle associated with this puzzle animation
|
||||
* waiter is still valid.
|
||||
*/
|
||||
public boolean puzzleStillValid ()
|
||||
{
|
||||
return (_puzobj.isInPlay() && (_roundId == _puzobj.roundId));
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected final void allAnimationsFinished ()
|
||||
{
|
||||
allAnimationsFinished(puzzleStillValid());
|
||||
}
|
||||
|
||||
/**
|
||||
* Replacement for {@link AnimationWaiter#allAnimationsFinished} that
|
||||
* also reports whether the puzzle associated with this animation
|
||||
* waiter is still valid.
|
||||
*/
|
||||
protected abstract void allAnimationsFinished (boolean puzStillValid);
|
||||
|
||||
/** The initial round id. */
|
||||
protected int _roundId;
|
||||
|
||||
/** The puzzle object that the animations we're observering want to
|
||||
* modify. */
|
||||
protected PuzzleObject _puzobj;
|
||||
}
|
||||
@@ -0,0 +1,461 @@
|
||||
//
|
||||
// $Id: PuzzleBoardView.java,v 1.1 2003/11/26 01:42:34 mdb Exp $
|
||||
|
||||
package com.threerings.puzzle.client;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.swing.UIManager;
|
||||
|
||||
import com.samskivert.swing.Label;
|
||||
import com.samskivert.swing.util.SwingUtil;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.media.VirtualMediaPanel;
|
||||
import com.threerings.media.animation.Animation;
|
||||
import com.threerings.media.animation.AnimationAdapter;
|
||||
import com.threerings.media.image.Mirage;
|
||||
import com.threerings.media.sprite.Sprite;
|
||||
import com.threerings.media.sprite.SpriteManager;
|
||||
|
||||
import com.threerings.yohoho.client.YoUI;
|
||||
import com.threerings.puzzle.Log;
|
||||
import com.threerings.puzzle.client.ScoreAnimation;
|
||||
import com.threerings.puzzle.data.Board;
|
||||
import com.threerings.puzzle.data.PuzzleConfig;
|
||||
import com.threerings.puzzle.util.PuzzleContext;
|
||||
|
||||
/**
|
||||
* The puzzle board view displays a view of a puzzle game.
|
||||
*/
|
||||
public abstract class PuzzleBoardView extends VirtualMediaPanel
|
||||
{
|
||||
/**
|
||||
* Constructs a puzzle board view.
|
||||
*/
|
||||
public PuzzleBoardView (PuzzleContext ctx)
|
||||
{
|
||||
super(ctx.getFrameManager());
|
||||
|
||||
// keep this for later
|
||||
_ctx = ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the board with the board dimensions.
|
||||
*/
|
||||
public void init (PuzzleConfig config)
|
||||
{
|
||||
// save off our bounds
|
||||
Dimension bounds = getPreferredSize();
|
||||
_bounds = new Rectangle(0, 0, bounds.width, bounds.height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the board to be displayed.
|
||||
*/
|
||||
public void setBoard (Board board)
|
||||
{
|
||||
_board = board;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the board view with a reference to its controller so that
|
||||
* it may communicate directly rather than by posting actions up the
|
||||
* interface hierarchy which sometimes fails if the puzzle board view
|
||||
* is hidden before we get a chance to post our actions.
|
||||
*/
|
||||
public void setController (PuzzleController pctrl)
|
||||
{
|
||||
_pctrl = pctrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the background image displayed by the board view.
|
||||
*/
|
||||
public void setBackgroundImage (Mirage image)
|
||||
{
|
||||
_background = image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this puzzle is paused or not.
|
||||
*/
|
||||
public void setPaused (boolean paused)
|
||||
{
|
||||
if (paused) {
|
||||
_pauseLabel = new Label(
|
||||
YoUI.puzzle.getBundle().get(_pctrl.getPauseString()),
|
||||
Label.BOLD | Label.OUTLINE, Color.WHITE, Color.BLACK,
|
||||
_fonts[_fonts.length - 2]);
|
||||
_pauseLabel.setTargetWidth(_bounds.width);
|
||||
_pauseLabel.layout(this);
|
||||
} else {
|
||||
_pauseLabel = null;
|
||||
}
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given animation to the set of animations currently present
|
||||
* on the board. The animation will be added to a list of action
|
||||
* animations whose count can be queried with {@link
|
||||
* #getActionAnimationCount}. The animation will automatically be
|
||||
* removed from the action list when it completes.
|
||||
*/
|
||||
public void addActionAnimation (Animation anim)
|
||||
{
|
||||
super.addAnimation(anim);
|
||||
|
||||
// remember the animation's existence
|
||||
_actionAnims.add(anim);
|
||||
|
||||
// and listen for it to finish so that we can clear it out
|
||||
anim.addAnimationObserver(_actionAnimObs);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void abortAnimation (Animation anim)
|
||||
{
|
||||
super.abortAnimation(anim);
|
||||
|
||||
// always check to see if it was action-y
|
||||
animationFinished(anim);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a potential action animation is finished.
|
||||
*/
|
||||
protected void animationFinished (Animation anim)
|
||||
{
|
||||
if (DEBUG_ACTION) {
|
||||
Log.info("Animation cleared " + StringUtil.shortClassName(anim) +
|
||||
":" + _actionAnims.contains(anim));
|
||||
}
|
||||
|
||||
// if it WAS an action animation, check for a clear
|
||||
if (_actionAnims.remove(anim)) {
|
||||
maybeFireCleared();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given sprite to the set of sprites currently present on
|
||||
* the board. The sprite will be added to a list of action sprites
|
||||
* whose count can be queried with {@link #getActionSpriteCount}. Callers
|
||||
* should be sure to remove the sprite when their work with it is done
|
||||
* via {@link #removeSprite}.
|
||||
*/
|
||||
public void addActionSprite (Sprite sprite)
|
||||
{
|
||||
// add the piece to the sprite manager
|
||||
addSprite(sprite);
|
||||
|
||||
// note that this piece is interesting
|
||||
_actionSprites.add(sprite);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given sprite from the board.
|
||||
*/
|
||||
public void removeSprite (Sprite sprite)
|
||||
{
|
||||
super.removeSprite(sprite);
|
||||
|
||||
if (DEBUG_ACTION) {
|
||||
Log.info("Sprite cleared " + StringUtil.shortClassName(sprite) +
|
||||
":" + _actionSprites.contains(sprite));
|
||||
}
|
||||
|
||||
// we just always check to see if it was action-y
|
||||
if (_actionSprites.remove(sprite)) {
|
||||
maybeFireCleared();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of action animations on the board.
|
||||
*/
|
||||
public int getActionAnimationCount ()
|
||||
{
|
||||
return _actionAnims.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of action sprites on the board.
|
||||
*/
|
||||
public int getActionSpriteCount ()
|
||||
{
|
||||
return _actionSprites.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the count of action sprites and animations on the board.
|
||||
*/
|
||||
public int getActionCount ()
|
||||
{
|
||||
return _actionSprites.size() + _actionAnims.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps to the logs, a list of interesting sprites and animations
|
||||
* currently active on the puzzle board.
|
||||
*/
|
||||
public void dumpActors ()
|
||||
{
|
||||
StringUtil.Formatter fmt = new StringUtil.Formatter() {
|
||||
public String toString (Object obj) {
|
||||
return StringUtil.shortClassName(obj);
|
||||
}
|
||||
};
|
||||
Log.info("Board contents [board=" + StringUtil.shortClassName(this) +
|
||||
", sprites=" + StringUtil.listToString(_actionSprites, fmt) +
|
||||
", anims=" + StringUtil.listToString(_actionAnims, fmt) +
|
||||
"].");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns an animation displaying the given string with
|
||||
* the specified parameters, floating it a short distance up the view.
|
||||
*
|
||||
* @param score the score text to display.
|
||||
* @param color the color of the text.
|
||||
* @param fontSize the size of the text; a value between 0 and {@link
|
||||
* #getPuzzleFontCount} - 1.
|
||||
* @param x the x-position at which the score is to be placed.
|
||||
* @param y the y-position at which the score is to be placed.
|
||||
*/
|
||||
public ScoreAnimation createScoreAnimation (
|
||||
String score, Color color, int fontSize, int x, int y)
|
||||
{
|
||||
// create and configure the label
|
||||
Label label = new Label(score);
|
||||
label.setTargetWidth(_bounds.width);
|
||||
label.setStyle(Label.OUTLINE);
|
||||
label.setTextColor(color);
|
||||
label.setAlternateColor(Color.black);
|
||||
label.setFont(getPuzzleFont(fontSize));
|
||||
label.setAlignment(Label.CENTER);
|
||||
label.layout(this);
|
||||
|
||||
// create the score animation
|
||||
ScoreAnimation anim = new ScoreAnimation(label, x, y);
|
||||
anim.setRenderOrder(getScoreRenderOrder());
|
||||
return anim;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the render order to be assigned to score animations by
|
||||
* default. Derived classes may wish to override this method to
|
||||
* change the default render order.
|
||||
*
|
||||
* @see #createScoreAnimation
|
||||
*/
|
||||
protected int getScoreRenderOrder ()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the puzzle font to be used for the specified score.
|
||||
*/
|
||||
public int getFont (String why, int score, int maxScore)
|
||||
{
|
||||
int fontSize = (int)(score*getPuzzleFontCount()/maxScore);
|
||||
fontSize = Math.min(FONT_SIZES.length-1, fontSize);
|
||||
// Log.info("Font for " + why + " (" + score + " of " + maxScore +
|
||||
// ") => " + fontSize + ".");
|
||||
return fontSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of different puzzle font sizes so that those who
|
||||
* care to choose a font size out of the range of possible sizes can
|
||||
* do so gracefully.
|
||||
*/
|
||||
public int getPuzzleFontCount ()
|
||||
{
|
||||
return FONT_SIZES.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Positions the supplied animation so as to avoid any active
|
||||
* animations previously registered with this method, and adds the
|
||||
* animation to the list of animations to be avoided by any future
|
||||
* avoid animations.
|
||||
*/
|
||||
public void trackAvoidAnimation (Animation anim)
|
||||
{
|
||||
// reposition the animation as appropriate
|
||||
Rectangle abounds = new Rectangle(anim.getBounds());
|
||||
ArrayList avoidables = (ArrayList)_avoidAnims.clone();
|
||||
if (SwingUtil.positionRect(abounds, _bounds, avoidables)) {
|
||||
anim.setLocation(abounds.x, abounds.y);
|
||||
}
|
||||
|
||||
// add the animation to the list of avoidables
|
||||
_avoidAnims.add(anim);
|
||||
// keep an eye on it so that we can remove it when it's finished
|
||||
anim.addAnimationObserver(_avoidAnimObs);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void paintBehind (Graphics2D gfx, Rectangle dirty)
|
||||
{
|
||||
super.paintBehind(gfx, dirty);
|
||||
|
||||
// render the background
|
||||
renderBackground(gfx, dirty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the background of the board with the background color.
|
||||
*/
|
||||
protected void renderBackground (Graphics2D gfx, Rectangle dirty)
|
||||
{
|
||||
gfx.setColor(getBackground());
|
||||
gfx.fill(dirty);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void paintBetween (Graphics2D gfx, Rectangle dirty)
|
||||
{
|
||||
super.paintBetween(gfx, dirty);
|
||||
// PerformanceMonitor.tick(this, "paint");
|
||||
renderBoard(gfx, dirty);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void paintInFront (Graphics2D gfx, Rectangle dirty)
|
||||
{
|
||||
super.paintInFront(gfx, dirty);
|
||||
|
||||
// if the action is paused, indicate as much
|
||||
if (_pauseLabel != null) {
|
||||
Dimension d = _pauseLabel.getSize();
|
||||
_pauseLabel.render(gfx, (_bounds.width - d.width) / 2,
|
||||
(_bounds.height - d.height) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires a {@link #ACTION_CLEARED} command iff we have no remaining
|
||||
* interesting sprites or animations.
|
||||
*/
|
||||
protected void maybeFireCleared ()
|
||||
{
|
||||
if (DEBUG_ACTION) {
|
||||
Log.info("Maybe firing cleared " +
|
||||
getActionCount() + ":" + isShowing());
|
||||
}
|
||||
if (getActionCount() == 0) {
|
||||
_pctrl.boardActionCleared();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the board contents to the given graphics context.
|
||||
* Sub-classes should implement this method to draw all of their
|
||||
* game-specific business.
|
||||
*/
|
||||
protected abstract void renderBoard (Graphics2D gfx, Rectangle dirty);
|
||||
|
||||
/**
|
||||
* Returns the puzzle font of the specified size.
|
||||
*
|
||||
* @param size the desired font size; a value between 0 and {@link
|
||||
* #getPuzzleFontCount} - 1.
|
||||
*/
|
||||
protected static Font getPuzzleFont (int size)
|
||||
{
|
||||
return _fonts[size];
|
||||
}
|
||||
|
||||
/** Our client context. */
|
||||
protected PuzzleContext _ctx;
|
||||
|
||||
/** Our puzzle controller. */
|
||||
protected PuzzleController _pctrl;
|
||||
|
||||
/** The board data to be displayed. */
|
||||
protected Board _board;
|
||||
|
||||
/** The board's bounding rectangle. */
|
||||
protected Rectangle _bounds;
|
||||
|
||||
/** The action animations on the board. */
|
||||
protected ArrayList _actionAnims = new ArrayList();
|
||||
|
||||
/** The action sprites on the board. */
|
||||
protected ArrayList _actionSprites = new ArrayList();
|
||||
|
||||
/** The animations that other animations may wish to avoid. */
|
||||
protected ArrayList _avoidAnims = new ArrayList();
|
||||
|
||||
/** Our background image. */
|
||||
protected Mirage _background;
|
||||
|
||||
/** A label to show when the puzzle is paused. */
|
||||
protected Label _pauseLabel;
|
||||
|
||||
/** The distance in pixels that score animations float. */
|
||||
protected int _scoreDist = DEFAULT_SCORE_DISTANCE;
|
||||
|
||||
/** Listens to our action animations and clears them when they're done. */
|
||||
protected AnimationAdapter _actionAnimObs = new AnimationAdapter() {
|
||||
public void animationCompleted (Animation anim, long when) {
|
||||
animationFinished(anim);
|
||||
}
|
||||
};
|
||||
|
||||
/** Automatically removes avoid animations when they're done. */
|
||||
protected AnimationAdapter _avoidAnimObs = new AnimationAdapter() {
|
||||
public void animationCompleted (Animation anim, long when) {
|
||||
if (!_avoidAnims.remove(anim)) {
|
||||
Log.warning("Couldn't remove avoid animation?! " + anim + ".");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** The puzzle fonts. */
|
||||
protected static Font[] _fonts;
|
||||
|
||||
/** Temporary action debugging. */
|
||||
protected static boolean DEBUG_ACTION = false;
|
||||
|
||||
// action state constants
|
||||
protected static final int ACTION_GOING = 0;
|
||||
protected static final int CLEAR_PENDING = 1;
|
||||
protected static final int ACTION_CLEARED = 2;
|
||||
|
||||
/** The default vertical distance to float score animations. */
|
||||
protected static final int DEFAULT_SCORE_DISTANCE = 30;
|
||||
|
||||
/** The mid-sized font used as the default size for score
|
||||
* animations. */
|
||||
protected static final int MEDIUM_FONT_SIZE = 1;
|
||||
|
||||
/** The puzzle font sizes. */
|
||||
protected static final double[] FONT_SIZES = {
|
||||
24d, 30d, 36d, 42d, 52d, 68d };
|
||||
|
||||
static {
|
||||
// create the puzzle fonts
|
||||
Font ofont = UIManager.getFont("Label.serifFont");
|
||||
ofont = ofont.deriveFont((float)FONT_SIZES[0]);
|
||||
_fonts = new Font[FONT_SIZES.length];
|
||||
for (int ii = 0; ii < _fonts.length; ii++) {
|
||||
double scale = FONT_SIZES[ii]/FONT_SIZES[0];
|
||||
_fonts[ii] = ofont.deriveFont(
|
||||
AffineTransform.getScaleInstance(scale * 1.1d, scale));
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,162 @@
|
||||
//
|
||||
// $Id: PuzzleControllerDelegate.java,v 1.1 2003/11/26 01:42:34 mdb Exp $
|
||||
|
||||
package com.threerings.puzzle.client;
|
||||
|
||||
import com.threerings.crowd.data.PlaceObject;
|
||||
|
||||
import com.threerings.parlor.game.GameControllerDelegate;
|
||||
|
||||
import com.threerings.puzzle.data.Board;
|
||||
import com.threerings.puzzle.data.BoardSummary;
|
||||
import com.threerings.puzzle.data.PuzzleCodes;
|
||||
import com.threerings.puzzle.data.PuzzleGameCodes;
|
||||
import com.threerings.puzzle.data.PuzzleObject;
|
||||
|
||||
/**
|
||||
* A base class for puzzle controller delegates. Provides access to some
|
||||
* delegated puzzle controller methods ({@link #startAction}, {@link
|
||||
* #clearAction}, etc.) and provides a casted reference to the puzzle
|
||||
* object.
|
||||
*/
|
||||
public class PuzzleControllerDelegate extends GameControllerDelegate
|
||||
implements PuzzleCodes, PuzzleGameCodes
|
||||
{
|
||||
/**
|
||||
* Constructs a puzzle controller delegate.
|
||||
*/
|
||||
public PuzzleControllerDelegate (PuzzleController ctrl)
|
||||
{
|
||||
super(ctrl);
|
||||
|
||||
// keep around a casted reference to our controller
|
||||
_ctrl = ctrl;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void willEnterPlace (PlaceObject plobj)
|
||||
{
|
||||
super.willEnterPlace(plobj);
|
||||
|
||||
// get a casted reference to our game object
|
||||
_puzobj = (PuzzleObject)plobj;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void didLeavePlace (PlaceObject plobj)
|
||||
{
|
||||
super.didLeavePlace(plobj);
|
||||
|
||||
_puzobj = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a player is knocked out of the game.
|
||||
*/
|
||||
public void playerKnockedOut (int pidx)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the user toggles chatting mode.
|
||||
*/
|
||||
public void setChatting (boolean chatting)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Can we start chatting at the instant that this method is called?
|
||||
*/
|
||||
protected boolean canStartChatting ()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Derived classes should override this method and do whatever is
|
||||
* necessary to start up the action for their puzzle. This could be
|
||||
* called when the user is already in the "room" and the game starts,
|
||||
* or immediately upon entering the room if the game is already
|
||||
* started (for example if they disconnected and reconnected to a game
|
||||
* already in progress).
|
||||
*/
|
||||
protected void startAction ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates that wish to postpone action clearing can override this
|
||||
* method to return false until such time as the action can be
|
||||
* cleared. They must, however, call {@link #maybeClearAction} when
|
||||
* conditions become such that they would once again allow action to
|
||||
* be cleared.
|
||||
*/
|
||||
protected boolean canClearAction ()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link PuzzleController#maybeClearAction}, preserving its
|
||||
* protected access but making the method available to all
|
||||
* PuzzleControllerDelegate derivations.
|
||||
*/
|
||||
protected void maybeClearAction ()
|
||||
{
|
||||
_ctrl.maybeClearAction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Puzzles should override this method and clear out any action on the
|
||||
* board and generally clean up anything that was going on because the
|
||||
* game was in play. This is called when the game has ended or when it
|
||||
* is going to reset and when the client leaves the game "room".
|
||||
* Anything that is cleared out here should be recreated in {@link
|
||||
* #startAction}.
|
||||
*/
|
||||
protected void clearAction ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the puzzle controller sets up a new board for the
|
||||
* player.
|
||||
*
|
||||
* @param board the newly initialized and ready-to-go board.
|
||||
*/
|
||||
public void setBoard (Board board)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the game is suspended while we wait for a new piece
|
||||
* packet from the server.
|
||||
*/
|
||||
public void didSuspend ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the game is resumed once a new piece packet arrives
|
||||
* from the server while we were suspended awaiting its arrival.
|
||||
*
|
||||
* @param delta the time elapsed in milliseconds while we were
|
||||
* suspended.
|
||||
*/
|
||||
public void didResume (long delta)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the puzzle difficulty level is changed.
|
||||
*/
|
||||
public void difficultyChanged (int level)
|
||||
{
|
||||
}
|
||||
|
||||
/** Our puzzle controller. */
|
||||
protected PuzzleController _ctrl;
|
||||
|
||||
/** The puzzle distributed object. */
|
||||
protected PuzzleObject _puzobj;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// $Id: PuzzleGameService.java,v 1.1 2003/11/26 01:42:34 mdb Exp $
|
||||
|
||||
package com.threerings.puzzle.client;
|
||||
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
|
||||
import com.threerings.puzzle.data.Board;
|
||||
import com.threerings.puzzle.data.PuzzleCodes;
|
||||
|
||||
/**
|
||||
* Provides services used by puzzle game clients to request that actions
|
||||
* be taken by the puzzle manager.
|
||||
*/
|
||||
public interface PuzzleGameService extends InvocationService, PuzzleCodes
|
||||
{
|
||||
/**
|
||||
* Asks the puzzle manager to apply the supplied progress events for
|
||||
* the specified puzzle round to the player's state.
|
||||
*/
|
||||
public void updateProgress (Client client, int roundId, int[] events);
|
||||
|
||||
/**
|
||||
* Debug variant of {@link #updateProgress} that is only used when
|
||||
* {@link #SYNC_BOARD_STATE} is true and which includes the board
|
||||
* states associated with each event.
|
||||
*/
|
||||
public void updateProgressSync (
|
||||
Client client, int roundId, int[] events, Board[] states);
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
//
|
||||
// $Id: PuzzlePanel.java,v 1.1 2003/11/26 01:42:34 mdb Exp $
|
||||
|
||||
package com.threerings.puzzle.client;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Graphics;
|
||||
import java.io.IOException;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import com.samskivert.swing.Controller;
|
||||
import com.samskivert.swing.ControllerProvider;
|
||||
import com.samskivert.swing.util.SwingUtil;
|
||||
|
||||
import com.threerings.media.image.Mirage;
|
||||
import com.threerings.util.KeyTranslator;
|
||||
import com.threerings.util.RobotPlayer;
|
||||
|
||||
import com.threerings.crowd.client.PlaceView;
|
||||
import com.threerings.crowd.data.PlaceObject;
|
||||
|
||||
import com.threerings.puzzle.Log;
|
||||
import com.threerings.puzzle.data.BoardSummary;
|
||||
import com.threerings.puzzle.data.PuzzleCodes;
|
||||
import com.threerings.puzzle.data.PuzzleConfig;
|
||||
import com.threerings.puzzle.data.PuzzleGameCodes;
|
||||
import com.threerings.puzzle.data.PuzzleObject;
|
||||
import com.threerings.puzzle.util.PuzzleContext;
|
||||
|
||||
/**
|
||||
* The puzzle panel class should be extended by classes that provide a
|
||||
* view for a puzzle game. The {@link PuzzleController} calls these
|
||||
* methods as necessary to perform its duties in managing the logical
|
||||
* actions of a puzzle game.
|
||||
*/
|
||||
public abstract class PuzzlePanel extends JPanel
|
||||
implements PlaceView, ControllerProvider, PuzzleCodes, PuzzleGameCodes
|
||||
{
|
||||
/**
|
||||
* Constructs a puzzle panel.
|
||||
*/
|
||||
public PuzzlePanel (PuzzleContext ctx, PuzzleController controller)
|
||||
{
|
||||
_ctx = ctx;
|
||||
_controller = controller;
|
||||
|
||||
// set up the keyboard manager
|
||||
_xlate = getKeyTranslator();
|
||||
_ctx.getKeyboardManager().setTarget(this, _xlate);
|
||||
|
||||
// leave the keyboard manager disabled to start, and set things up
|
||||
// for chatting
|
||||
setPuzzleGrabsKeys(false);
|
||||
|
||||
// configure the puzzle panel
|
||||
setLayout(new BorderLayout());
|
||||
|
||||
// create the puzzle board view
|
||||
_bview = createBoardView(ctx);
|
||||
|
||||
// create the puzzle board panel
|
||||
_bpanel = createBoardPanel();
|
||||
|
||||
// add the board panel
|
||||
add(_bpanel, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporarily replaces the puzzle board display with the supplied
|
||||
* overlay panel. The panel can be removed and the board display
|
||||
* restored by calling {@link #popOverlayPanel}.
|
||||
*
|
||||
* @return true if the specified panel will be displayed, false if it
|
||||
* was not able to be pushed because another overlay panel is already
|
||||
* pushed onto the primary panel.
|
||||
*/
|
||||
public boolean pushOverlayPanel (JPanel opanel)
|
||||
{
|
||||
// bail if we've already got an overlay
|
||||
if (_opanel != null) {
|
||||
Log.info("Refusing to push overlay panel, we've already got one " +
|
||||
"[opanel=" + _opanel + ", npanel=" + opanel + "].");
|
||||
return false;
|
||||
}
|
||||
|
||||
// swap in the overlay panel
|
||||
_opanel = opanel;
|
||||
remove(_bpanel);
|
||||
add(_opanel);
|
||||
|
||||
// make sure the UI updates
|
||||
SwingUtil.refresh(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pops the overlay panel off of the main puzzle board display.
|
||||
*/
|
||||
public void popOverlayPanel ()
|
||||
{
|
||||
if (_opanel != null) {
|
||||
remove(_opanel);
|
||||
_opanel = null;
|
||||
add(_bpanel);
|
||||
|
||||
// make sure the UI updates
|
||||
SwingUtil.refresh(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the puzzle panel with the puzzle config of the puzzle
|
||||
* whose user interface is being displayed by the panel
|
||||
*/
|
||||
public void init (PuzzleConfig config)
|
||||
{
|
||||
_config = config;
|
||||
_bview.init(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether this panel receives events periodically from a robot
|
||||
* player.
|
||||
*/
|
||||
public void setRobotPlayer (boolean isrobot)
|
||||
{
|
||||
// create a robot player if necessary
|
||||
if (_robot == null) {
|
||||
_robot = new RobotPlayer(this, _xlate);
|
||||
}
|
||||
setPuzzleGrabsKeys(!isrobot);
|
||||
_robot.setRobotDelay(200L);
|
||||
_robot.setActive(isrobot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the puzzle grabs keys or if they should go to the chat
|
||||
* window.
|
||||
*/
|
||||
public void setPuzzleGrabsKeys (boolean puzgrabs)
|
||||
{
|
||||
// enable or disable the key manager appropriately
|
||||
_ctx.getKeyboardManager().setEnabled(puzgrabs);
|
||||
if (puzgrabs) {
|
||||
getBoardView().requestFocus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the controller when the action starts.
|
||||
*/
|
||||
public void startAction ()
|
||||
{
|
||||
// make the first player a robot player
|
||||
if (ROBOT_TEST && _controller.getPlayerIndex() == 0) {
|
||||
setRobotPlayer(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the controller when the action stops.
|
||||
*/
|
||||
public void clearAction ()
|
||||
{
|
||||
// deactivate the robot player
|
||||
if (ROBOT_TEST && _controller.getPlayerIndex() == 0) {
|
||||
setRobotPlayer(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the puzzle board view that will be used to display the main
|
||||
* puzzle interface. This is called when the puzzle panel is
|
||||
* constructed. The derived panel will still be responsible for adding
|
||||
* the board to the interface hierarchy.
|
||||
*/
|
||||
protected abstract PuzzleBoardView createBoardView (PuzzleContext ctx);
|
||||
|
||||
/**
|
||||
* Creates the main panel used to display the puzzle and its various
|
||||
* in-game accoutrements (next block views, player status displays,
|
||||
* etc.) This is called when the puzzle panel is constructed. The
|
||||
* derived panel is responsible for making sure that the board view is
|
||||
* present in the board panel.
|
||||
*/
|
||||
protected abstract JPanel createBoardPanel ();
|
||||
|
||||
/**
|
||||
* Returns a key translator with the desired key to controller command
|
||||
* mappings desired for this puzzle.
|
||||
*/
|
||||
protected abstract KeyTranslator getKeyTranslator ();
|
||||
|
||||
// documentation inherited from interface
|
||||
public void willEnterPlace (PlaceObject plobj)
|
||||
{
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void didLeavePlace (PlaceObject plobj)
|
||||
{
|
||||
// disable the keyboard manager when we leave
|
||||
_ctx.getKeyboardManager().reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the {@link PuzzleBoardView} in use.
|
||||
*/
|
||||
public PuzzleBoardView getBoardView ()
|
||||
{
|
||||
return _bview;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public Controller getController ()
|
||||
{
|
||||
return _controller;
|
||||
}
|
||||
|
||||
/** Our puzzle context. */
|
||||
protected PuzzleContext _ctx;
|
||||
|
||||
/** The board view on which the primary puzzle interface is displayed. */
|
||||
protected PuzzleBoardView _bview;
|
||||
|
||||
/** The board panel displayed while the puzzle is in progress. */
|
||||
protected JPanel _bpanel;
|
||||
|
||||
/** The board overlay panel displayed as requested. */
|
||||
protected JPanel _opanel;
|
||||
|
||||
/** The puzzle config. */
|
||||
protected PuzzleConfig _config;
|
||||
|
||||
/** The robot player. */
|
||||
protected RobotPlayer _robot;
|
||||
|
||||
/** Our key translations. */
|
||||
protected KeyTranslator _xlate;
|
||||
|
||||
/** The puzzle game controller. */
|
||||
protected PuzzleController _controller;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// $Id: PuzzleService.java,v 1.1 2003/11/26 01:42:34 mdb Exp $
|
||||
|
||||
package com.threerings.puzzle.client;
|
||||
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
|
||||
import com.threerings.crowd.data.PlaceConfig;
|
||||
import com.threerings.puzzle.data.SolitairePuzzleConfig;
|
||||
|
||||
/**
|
||||
* The puzzle services provide a mechanism by which the client can enter
|
||||
* and leave puzzles. These services should not be used directly, but
|
||||
* instead should be accessed via the {@link PuzzleDirector}.
|
||||
*/
|
||||
public interface PuzzleService extends InvocationService
|
||||
{
|
||||
/** Used to communicate responses to {@link #enterPuzzle} requests. */
|
||||
public static interface EnterPuzzleListener extends InvocationListener
|
||||
{
|
||||
/**
|
||||
* Indicates that a {@link #enterPuzzle} request was successful
|
||||
* and provides the place config for the puzzle room.
|
||||
*/
|
||||
public void puzzleEntered (PlaceConfig config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that this client start up the specified single-player
|
||||
* puzzle.
|
||||
*/
|
||||
public void startPuzzle (Client client, SolitairePuzzleConfig config,
|
||||
InvocationListener listener);
|
||||
|
||||
/**
|
||||
* Requests that this client enter the specified puzzle.
|
||||
*/
|
||||
public void enterPuzzle (
|
||||
Client client, int puzzleOid, EnterPuzzleListener listener);
|
||||
|
||||
/**
|
||||
* Requests that this client depart whatever puzzle they occupy.
|
||||
*/
|
||||
public void leavePuzzle (Client client);
|
||||
|
||||
/**
|
||||
* Requests that the difficulty level of the puzzle this client is
|
||||
* currently occupying be changed to the specified difficulty level.
|
||||
*/
|
||||
public void changeDifficulty (Client client, int level);
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
//
|
||||
// $Id: ScoreAnimation.java,v 1.1 2003/11/26 01:42:34 mdb Exp $
|
||||
|
||||
package com.threerings.puzzle.client;
|
||||
|
||||
import java.awt.AlphaComposite;
|
||||
import java.awt.Color;
|
||||
import java.awt.Composite;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
|
||||
import com.samskivert.swing.Label;
|
||||
|
||||
import com.threerings.media.animation.Animation;
|
||||
|
||||
import com.threerings.puzzle.Log;
|
||||
|
||||
public class ScoreAnimation extends Animation
|
||||
{
|
||||
/**
|
||||
* Constructs a score animation for the given score value centered at
|
||||
* the given coordinates.
|
||||
*/
|
||||
public ScoreAnimation (Label label, int x, int y)
|
||||
{
|
||||
this(label, x, y, DEFAULT_FLOAT_PERIOD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a score animation for the given score value centered at
|
||||
* the given coordinates. The animation will float up the screen for
|
||||
* 30 pixels.
|
||||
*/
|
||||
public ScoreAnimation (Label label, int x, int y, long floatPeriod)
|
||||
{
|
||||
this(label, x, y, x, y - DELTA_Y, floatPeriod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a score animation for the given score value starting at
|
||||
* the given coordinates and floating toward the specified
|
||||
* coordinates.
|
||||
*/
|
||||
public ScoreAnimation (Label label, int sx, int sy,
|
||||
int destx, int desty, long floatPeriod)
|
||||
{
|
||||
super(new Rectangle(sx, sy, label.getSize().width,
|
||||
label.getSize().height));
|
||||
|
||||
// save things off
|
||||
_label = label;
|
||||
_startX = _x = sx;
|
||||
_startY = _y = sy;
|
||||
_destx = destx;
|
||||
_desty = desty;
|
||||
_floatPeriod = floatPeriod;
|
||||
|
||||
// calculate our deltas
|
||||
_dx = (destx - sx);
|
||||
_dy = (desty - sy);
|
||||
|
||||
// initialize our starting alpha
|
||||
_alpha = 1.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to change the direction 180 degrees.
|
||||
*/
|
||||
public void flipDirection ()
|
||||
{
|
||||
_dx = -_dx;
|
||||
_dy = -_dy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the label used to render this score animation.
|
||||
*/
|
||||
public Label getLabel ()
|
||||
{
|
||||
return _label;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void setLocation (int x, int y)
|
||||
{
|
||||
super.setLocation(x, y);
|
||||
|
||||
// update our destination coordinates
|
||||
_destx += (x - _startX);
|
||||
_desty += (y - _startY);
|
||||
|
||||
// update our initial coordinates
|
||||
_startX = _x = x;
|
||||
_startY = _y = y;
|
||||
|
||||
// recalculate our deltas
|
||||
_dx = (_destx - x);
|
||||
_dy = (_desty - y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the duration of this score animation to the specified time in
|
||||
* milliseconds. This should be called before the animation is added
|
||||
* to the animation manager.
|
||||
*/
|
||||
public void setFloatPeriod (long floatPeriod)
|
||||
{
|
||||
_floatPeriod = floatPeriod;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void tick (long timestamp)
|
||||
{
|
||||
boolean invalid = false;
|
||||
if (_start == 0) {
|
||||
// initialize our starting time
|
||||
_start = timestamp;
|
||||
// we need to make sure to invalidate ourselves initially
|
||||
invalid = true;
|
||||
}
|
||||
|
||||
long fadeDelay = _floatPeriod/2;
|
||||
long fadePeriod = _floatPeriod - fadeDelay;
|
||||
|
||||
// figure out the current alpha
|
||||
long msecs = timestamp - _start;
|
||||
float oalpha = _alpha;
|
||||
if (msecs > fadeDelay) {
|
||||
long rmsecs = msecs - fadeDelay;
|
||||
_alpha = Math.max(1.0f - (rmsecs / (float)fadePeriod), 0.0f);
|
||||
_alpha = Math.min(_alpha, 1.0f);
|
||||
}
|
||||
|
||||
// get the alpha composite
|
||||
_comp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, _alpha);
|
||||
|
||||
// determine the new y-position of the score
|
||||
float pctdone = (float)msecs / _floatPeriod;
|
||||
int ox = _x, oy = _y;
|
||||
_x = _startX + (int)(_dx * pctdone);
|
||||
_y = _startY + (int)(_dy * pctdone);
|
||||
|
||||
// only update our location and dirty ourselves if we actually
|
||||
// moved or our alpha changed
|
||||
if (ox != _x || oy != _y) {
|
||||
// dirty our old location
|
||||
invalidate();
|
||||
_bounds.setLocation(_x, _y);
|
||||
invalid = true;
|
||||
|
||||
} else if (oalpha != _alpha) {
|
||||
invalid = true;
|
||||
}
|
||||
|
||||
if (invalid) {
|
||||
// dirty our current location
|
||||
invalidate();
|
||||
}
|
||||
|
||||
// note whether we're done
|
||||
_finished = (msecs >= _floatPeriod);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void fastForward (long timeDelta)
|
||||
{
|
||||
if (_start > 0) {
|
||||
_start += timeDelta;
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void paint (Graphics2D gfx)
|
||||
{
|
||||
Composite ocomp = gfx.getComposite();
|
||||
if (_comp != null) {
|
||||
gfx.setComposite(_comp);
|
||||
}
|
||||
_label.render(gfx, _x, _y);
|
||||
gfx.setComposite(ocomp);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void toString (StringBuffer buf)
|
||||
{
|
||||
super.toString(buf);
|
||||
|
||||
buf.append(", x=").append(_x);
|
||||
buf.append(", y=").append(_y);
|
||||
buf.append(", alpha=").append(_alpha);
|
||||
}
|
||||
|
||||
/** The starting animation time. */
|
||||
protected long _start;
|
||||
|
||||
/** The label we're animating. */
|
||||
protected Label _label;
|
||||
|
||||
/** The starting coordinates of the score. */
|
||||
protected int _startX, _startY;
|
||||
|
||||
/** The current coordinates of the score. */
|
||||
protected int _x, _y;
|
||||
|
||||
/** The destination coordinates towards which the animation travels. */
|
||||
protected int _destx, _desty;
|
||||
|
||||
/** The distance to be traveled by the score animation. */
|
||||
protected int _dx, _dy;
|
||||
|
||||
/** The duration for which we float up the screen. */
|
||||
protected long _floatPeriod;
|
||||
|
||||
/** The current alpha level used to render the score. */
|
||||
protected float _alpha;
|
||||
|
||||
/** The composite used to render the score. */
|
||||
protected Composite _comp;
|
||||
|
||||
/** The time in milliseconds during which the score is visible. */
|
||||
protected static final long DEFAULT_FLOAT_PERIOD = 1500L;
|
||||
|
||||
/** The total vertical distance the score travels. */
|
||||
protected static final int DELTA_Y = 30;
|
||||
}
|
||||
Reference in New Issue
Block a user