Behold Vilya, Ring of Air and repository for our game and virtual worldly
extensions to the distributed environment provided by Narya. git-svn-id: svn+ssh://src.earth.threerings.net/vilya/trunk@1 c613c5cb-e716-0410-b11b-feb51c14d237
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// $Id: Log.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle;
|
||||
|
||||
/**
|
||||
* A placeholder class that contains a reference to the log object used by
|
||||
* this package.
|
||||
*/
|
||||
public class Log
|
||||
{
|
||||
public static com.samskivert.util.Log log =
|
||||
new com.samskivert.util.Log("puzzle");
|
||||
|
||||
/** Convenience function. */
|
||||
public static void debug (String message)
|
||||
{
|
||||
log.debug(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void info (String message)
|
||||
{
|
||||
log.info(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void warning (String message)
|
||||
{
|
||||
log.warning(message);
|
||||
}
|
||||
|
||||
/** Convenience function. */
|
||||
public static void logStackTrace (Throwable t)
|
||||
{
|
||||
log.logStackTrace(com.samskivert.util.Log.WARNING, t);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
//
|
||||
// $Id: PlayerStatusView.java 3664 2005-07-28 21:10:11Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.client;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.parlor.game.data.GameObject;
|
||||
|
||||
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)
|
||||
{
|
||||
if (_status != status) {
|
||||
_status = status;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to highlight the player status display when rendered.
|
||||
*/
|
||||
public void setHighlighted (boolean highlight)
|
||||
{
|
||||
if (_highlight != 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 Name _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,74 @@
|
||||
//
|
||||
// $Id: PuzzleAnimationWaiter.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.client;
|
||||
|
||||
import com.threerings.media.animation.AnimationWaiter;
|
||||
|
||||
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,420 @@
|
||||
//
|
||||
// $Id: PuzzleBoardView.java 3854 2006-02-14 22:17:52Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.client;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
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.animation.AnimationArranger;
|
||||
import com.threerings.media.image.Mirage;
|
||||
import com.threerings.media.sprite.Sprite;
|
||||
|
||||
import com.threerings.parlor.media.ScoreAnimation;
|
||||
|
||||
import com.threerings.puzzle.Log;
|
||||
import com.threerings.puzzle.data.Board;
|
||||
import com.threerings.puzzle.data.PuzzleCodes;
|
||||
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.
|
||||
* If paused, a label will be displayed with the component's font,
|
||||
* which may be set with setFont().
|
||||
*/
|
||||
public void setPaused (boolean paused)
|
||||
{
|
||||
if (paused) {
|
||||
String pmsg = _pctrl.getPauseString();
|
||||
pmsg = _ctx.getMessageManager().getBundle(
|
||||
PuzzleCodes.PUZZLE_MESSAGE_BUNDLE).xlate(pmsg);
|
||||
// create a label using our component's standard font
|
||||
_pauseLabel = new Label(pmsg, Label.BOLD | Label.OUTLINE,
|
||||
Color.WHITE, Color.BLACK,
|
||||
getFont());
|
||||
_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();
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void clearSprites ()
|
||||
{
|
||||
super.clearSprites();
|
||||
_actionSprites.clear();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void clearAnimations ()
|
||||
{
|
||||
super.clearAnimations();
|
||||
_actionAnims.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 font the font whith which to create the score animation.
|
||||
* @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, Font font, int x, int y)
|
||||
{
|
||||
return createScoreAnimation(
|
||||
ScoreAnimation.createLabel(score, color, font, this), x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a score animation, allowing derived classes to use custom
|
||||
* animations that are customized following a call to
|
||||
* {@link #createScoreAnimation}.
|
||||
*/
|
||||
protected ScoreAnimation createScoreAnimation (Label label, int x, int y)
|
||||
{
|
||||
return new ScoreAnimation(label, x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
// lazy init the arranger
|
||||
if (_avoidArranger == null) {
|
||||
_avoidArranger = new AnimationArranger();
|
||||
}
|
||||
_avoidArranger.positionAvoidAnimation(anim, _vbounds);
|
||||
}
|
||||
|
||||
// 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,
|
||||
_vbounds.x + (_vbounds.width - d.width) / 2,
|
||||
_vbounds.y + (_vbounds.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) {
|
||||
// we're probably in the middle of a tick() in an
|
||||
// animationDidFinish() call and we want everyone to finish
|
||||
// processing their business before we go clearing the action,
|
||||
// so we queue this up to be run after the tick is complete
|
||||
_ctx.getClient().getRunQueue().postRunnable(new Runnable() {
|
||||
public void run () {
|
||||
_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);
|
||||
|
||||
/** 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();
|
||||
|
||||
/** Prevents certain animations from overlapping others. */
|
||||
protected AnimationArranger _avoidArranger;
|
||||
|
||||
/** 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);
|
||||
}
|
||||
};
|
||||
|
||||
/** 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;
|
||||
}
|
||||
@@ -0,0 +1,978 @@
|
||||
//
|
||||
// $Id: PuzzleController.java 4145 2006-05-24 01:24:24Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.client;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.KeyListener;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.samskivert.swing.util.MouseHijacker;
|
||||
|
||||
import com.samskivert.util.CollectionUtil;
|
||||
import com.samskivert.util.ObserverList;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.media.FrameParticipant;
|
||||
|
||||
import com.threerings.presents.dobj.AttributeChangeListener;
|
||||
import com.threerings.presents.dobj.AttributeChangedEvent;
|
||||
import com.threerings.presents.dobj.ElementUpdateListener;
|
||||
import com.threerings.presents.dobj.ElementUpdatedEvent;
|
||||
|
||||
import com.threerings.crowd.client.PlaceControllerDelegate;
|
||||
import com.threerings.crowd.data.PlaceObject;
|
||||
|
||||
import com.threerings.parlor.game.client.GameController;
|
||||
import com.threerings.parlor.game.data.GameObject;
|
||||
|
||||
import com.threerings.puzzle.Log;
|
||||
import com.threerings.puzzle.data.Board;
|
||||
import com.threerings.puzzle.data.PuzzleCodes;
|
||||
import com.threerings.puzzle.data.PuzzleConfig;
|
||||
import com.threerings.puzzle.data.PuzzleObject;
|
||||
import com.threerings.puzzle.util.PuzzleContext;
|
||||
|
||||
/**
|
||||
* The puzzle game controller handles logical actions for a puzzle game.
|
||||
*/
|
||||
public abstract class PuzzleController extends GameController
|
||||
implements PuzzleCodes
|
||||
{
|
||||
/** The action command to toggle chatting mode. */
|
||||
public static final String TOGGLE_CHATTING = "toggle_chat";
|
||||
|
||||
/** Used by {@link PuzzleController#fireWhenActionCleared}. */
|
||||
public static interface ClearPender
|
||||
{
|
||||
/** {@link #actionCleared} return code. */
|
||||
public static final int RESTART_ACTION = -1;
|
||||
|
||||
/** {@link #actionCleared} return code. */
|
||||
public static final int CARE_NOT = 0;
|
||||
|
||||
/** {@link #actionCleared} return code. */
|
||||
public static final int NO_RESTART_ACTION = 1;
|
||||
|
||||
/**
|
||||
* Called when the action is fully cleared.
|
||||
*
|
||||
* @return One of {@link #RESTART_ACTION}, {@link #CARE_NOT} or
|
||||
* {@link #NO_RESTART_ACTION}.
|
||||
*/
|
||||
public int actionCleared ();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void didInit ()
|
||||
{
|
||||
super.didInit();
|
||||
|
||||
_panel = (PuzzlePanel)_view;
|
||||
_pctx = (PuzzleContext)_ctx;
|
||||
|
||||
// initialize the puzzle panel
|
||||
_puzconfig = (PuzzleConfig)_config;
|
||||
_panel.init(_puzconfig);
|
||||
|
||||
// initialize the board view
|
||||
_pview = _panel.getBoardView();
|
||||
_pview.setController(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns a new board model.
|
||||
*/
|
||||
protected abstract Board newBoard ();
|
||||
|
||||
/**
|
||||
* Returns the board associated with the puzzle.
|
||||
*/
|
||||
public Board getBoard ()
|
||||
{
|
||||
return _pboard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the player's index in the list of players for the game.
|
||||
*/
|
||||
public int getPlayerIndex ()
|
||||
{
|
||||
return _pidx;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void setGameOver (boolean gameOver)
|
||||
{
|
||||
super.setGameOver(gameOver);
|
||||
|
||||
// clear the action if we're informed that the game is over early
|
||||
// by the client
|
||||
if (gameOver) {
|
||||
clearAction();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the puzzle has action, false if the action is
|
||||
* cleared or it is suspended.
|
||||
*/
|
||||
public boolean hasAction ()
|
||||
{
|
||||
return (_astate == ACTION_GOING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether we're focusing on the chat window rather than the puzzle.
|
||||
*/
|
||||
public void setChatting (boolean chatting)
|
||||
{
|
||||
// ignore the request if we're already there
|
||||
if ((isChatting() == chatting) ||
|
||||
// ..or if we want to initiate chatting and..
|
||||
// we either can't right now or we don't have action
|
||||
(chatting && (!canStartChatting() || !hasAction()))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// update the panel
|
||||
_panel.setPuzzleGrabsKeys(!chatting);
|
||||
|
||||
// if we're moving focus to chat..
|
||||
if (chatting) {
|
||||
if (_unpauser != null) {
|
||||
Log.warning("Huh? Already have a mouse unpauser?");
|
||||
_unpauser.release();
|
||||
}
|
||||
_unpauser = new Unpauser(_panel);
|
||||
|
||||
} else {
|
||||
if (_unpauser != null) {
|
||||
_unpauser.release();
|
||||
_unpauser = null;
|
||||
}
|
||||
}
|
||||
|
||||
// update the chatting state
|
||||
_chatting = chatting;
|
||||
|
||||
// dispatch the change to our delegates
|
||||
applyToDelegates(PuzzleControllerDelegate.class, new DelegateOp() {
|
||||
public void apply (PlaceControllerDelegate delegate) {
|
||||
((PuzzleControllerDelegate)delegate).setChatting(_chatting);
|
||||
}
|
||||
});
|
||||
|
||||
// and check if we should be suspending the action during this pause
|
||||
if (supportsActionPause()) {
|
||||
// clear the action if we're pausing, resume it if we're
|
||||
// unpausing
|
||||
if (chatting) {
|
||||
clearAction();
|
||||
} else {
|
||||
safeStartAction();
|
||||
}
|
||||
_pview.setPaused(chatting);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the (untranslated) string to display when the puzzle is paused.
|
||||
*/
|
||||
public String getPauseString ()
|
||||
{
|
||||
return "m.paused";
|
||||
}
|
||||
|
||||
/**
|
||||
* Derived classes should override this and return false if their
|
||||
* action should not be paused when the user switches control to the
|
||||
* chat area.
|
||||
*/
|
||||
protected boolean supportsActionPause ()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can we start chatting at this juncture?
|
||||
*/
|
||||
protected boolean canStartChatting ()
|
||||
{
|
||||
// check with the delegates
|
||||
final boolean[] canChatNow = new boolean[] { true };
|
||||
applyToDelegates(PuzzleControllerDelegate.class, new DelegateOp() {
|
||||
public void apply (PlaceControllerDelegate delegate) {
|
||||
canChatNow[0] =
|
||||
((PuzzleControllerDelegate)delegate).canStartChatting() &&
|
||||
canChatNow[0];
|
||||
}
|
||||
});
|
||||
return canChatNow[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the puzzle has been defocused because the player
|
||||
* is doing some chatting.
|
||||
*/
|
||||
public boolean isChatting ()
|
||||
{
|
||||
return _chatting;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void willEnterPlace (PlaceObject plobj)
|
||||
{
|
||||
super.willEnterPlace(plobj);
|
||||
|
||||
// get a casted reference to our puzzle object
|
||||
_puzobj = (PuzzleObject)plobj;
|
||||
_puzobj.addListener(_kolist);
|
||||
_puzobj.addListener(_mlist);
|
||||
|
||||
// listen to key events..
|
||||
_pctx.getKeyDispatcher().addGlobalKeyListener(_globalKeyListener);
|
||||
|
||||
// save off our player index
|
||||
_pidx = _puzobj.getPlayerIndex(_pctx.getUsername());
|
||||
|
||||
// generate the starting board
|
||||
generateNewBoard();
|
||||
|
||||
// if the game is already in play, start up the action
|
||||
if (_puzobj.isInPlay() && _puzobj.isActivePlayer(_pidx)) {
|
||||
startAction();
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void mayLeavePlace (PlaceObject plobj)
|
||||
{
|
||||
super.mayLeavePlace(plobj);
|
||||
|
||||
// flush any pending progress events
|
||||
sendProgressUpdate();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void didLeavePlace (PlaceObject plobj)
|
||||
{
|
||||
super.didLeavePlace(plobj);
|
||||
|
||||
// clean up and clear out
|
||||
clearAction();
|
||||
|
||||
// stop listening to key events..
|
||||
_pctx.getKeyDispatcher().removeGlobalKeyListener(_globalKeyListener);
|
||||
|
||||
// clear out the puzzle object
|
||||
if (_puzobj != null) {
|
||||
_puzobj.removeListener(_mlist);
|
||||
_puzobj.removeListener(_kolist);
|
||||
_puzobj = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Puzzles that do not have "action" that starts and stops (via {@link
|
||||
* #startAction} and {@link #clearAction}) when the puzzle starts and
|
||||
* stops can override this method and return false.
|
||||
*/
|
||||
protected boolean isActionPuzzle ()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the action should start immediately as a result
|
||||
* of {@link #gameDidStart} being called. If a puzzle wishes to do
|
||||
* some beginning of the game fun stuff, like display a tutorial
|
||||
* screen, they can veto the action start and then start it themselves
|
||||
* later.
|
||||
*/
|
||||
protected boolean startActionImmediately ()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void attributeChanged (AttributeChangedEvent event)
|
||||
{
|
||||
String name = event.getName();
|
||||
|
||||
// deal with game state changes
|
||||
if (name.equals(PuzzleObject.STATE)) {
|
||||
switch (event.getIntValue()) {
|
||||
case PuzzleObject.IN_PLAY:
|
||||
// we have to postpone all game starting activity until the
|
||||
// current action has ended; only after all the animations have
|
||||
// been completed will everything be in a state fit for
|
||||
// starting back up again
|
||||
fireWhenActionCleared(new ClearPender() {
|
||||
public int actionCleared () {
|
||||
// do the standard game did start business
|
||||
gameDidStart();
|
||||
// we don't always start the action immediately
|
||||
return startActionImmediately() ?
|
||||
RESTART_ACTION : NO_RESTART_ACTION;
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case PuzzleObject.GAME_OVER:
|
||||
// similarly we haev to postpone game ending activity until
|
||||
// the current action has ended
|
||||
// clean up and clear out
|
||||
clearAction();
|
||||
// wait until the action is cleared before we roll down to our
|
||||
// delegates and do all that business
|
||||
fireWhenActionCleared(new ClearPender() {
|
||||
public int actionCleared () {
|
||||
gameDidEnd();
|
||||
return CARE_NOT;
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
super.attributeChanged(event);
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (name.equals(PuzzleObject.ROUND_ID)) {
|
||||
// Need to clear out stale events. If we don't, we could send
|
||||
// events that claim to be from the new round that are actually
|
||||
// from the old round.
|
||||
_events.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void gameWillReset ()
|
||||
{
|
||||
super.gameWillReset();
|
||||
|
||||
// stop the old action
|
||||
clearAction();
|
||||
|
||||
// when the server gets around to resetting the game, we'll get a
|
||||
// 'state => IN_PLAY' message which will result in gameDidStart()
|
||||
// being called and starting the action back up
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a new board is set.
|
||||
*/
|
||||
public void setBoard (Board board)
|
||||
{
|
||||
// we don't need to do anything by default
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ()
|
||||
{
|
||||
// do nothing if we're not an action puzzle
|
||||
if (!isActionPuzzle()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// refuse to start the action if our puzzle view is hidden
|
||||
if (_pidx != -1 && !_panel.getBoardView().isShowing()) {
|
||||
Log.warning("Refusing to start action on hidden puzzle.");
|
||||
Thread.dumpStack();
|
||||
return;
|
||||
}
|
||||
|
||||
// refuse to start the action if it's already going
|
||||
if (_astate != ACTION_CLEARED) {
|
||||
Log.warning("Action state inappropriate for startAction() " +
|
||||
"[astate=" + _astate + "].");
|
||||
Thread.dumpStack();
|
||||
return;
|
||||
}
|
||||
|
||||
if (isChatting() && supportsActionPause()) {
|
||||
Log.info("Not starting action, player is chatting in a puzzle " +
|
||||
"that supports pausing the action.");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.debug("Starting puzzle action.");
|
||||
|
||||
// register the game progress updater; it may already be updated
|
||||
// because we can cycle through clearing the action and starting
|
||||
// it again before the updater gets a chance to unregister itself
|
||||
if (!_pctx.getFrameManager().isRegisteredFrameParticipant(_updater)) {
|
||||
_pctx.getFrameManager().registerFrameParticipant(_updater);
|
||||
}
|
||||
|
||||
// make a note that we've started the action
|
||||
_astate = ACTION_GOING;
|
||||
|
||||
// let our panel know what's up
|
||||
_panel.startAction();
|
||||
|
||||
// and if we're not currently chatting, set the puzzle to grab
|
||||
// keys and for the chatbox to look disabled
|
||||
if (!isChatting()) {
|
||||
_panel.setPuzzleGrabsKeys(true);
|
||||
}
|
||||
|
||||
// let our delegates do their business
|
||||
applyToDelegates(PuzzleControllerDelegate.class, new DelegateOp() {
|
||||
public void apply (PlaceControllerDelegate delegate) {
|
||||
((PuzzleControllerDelegate)delegate).startAction();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* If it is not known whether the puzzle board view has finished
|
||||
* animating its final bits after a previous call to {@link
|
||||
* #clearAction}, this method should be used instead of {@link
|
||||
* #startAction} as it will wait until the action is confirmedly over
|
||||
* before starting it anew.
|
||||
*/
|
||||
protected void safeStartAction ()
|
||||
{
|
||||
// do nothing if we're not an action puzzle
|
||||
if (!isActionPuzzle()) {
|
||||
return;
|
||||
}
|
||||
|
||||
fireWhenActionCleared(new ClearPender() {
|
||||
public int actionCleared () {
|
||||
return RESTART_ACTION;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the game has ended or when it is going to reset and
|
||||
* when the client leaves the game "room". This method does not always
|
||||
* immediately clear the action, but may mark the clear as pending if
|
||||
* the action cannot yet be cleared (as indicated by {@link
|
||||
* #canClearAction}). The action will eventually be cleared which will
|
||||
* result in a call to {@link #actuallyClearAction} which is what
|
||||
* derived classes should override to do their action clearing
|
||||
* business.
|
||||
*/
|
||||
protected void clearAction ()
|
||||
{
|
||||
// do nothing if we're not an action puzzle
|
||||
if (!isActionPuzzle()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// no need to clear if we're already cleared or clearing
|
||||
if (_astate == CLEAR_PENDING || _astate == ACTION_CLEARED) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log.debug("Attempting to clear puzzle action.");
|
||||
|
||||
// put ourselves into a pending clear state and attempt to clear
|
||||
// the action
|
||||
_astate = CLEAR_PENDING;
|
||||
maybeClearAction();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by the {@link PuzzleBoardView} when all
|
||||
* action on the board has finished.
|
||||
*/
|
||||
protected void boardActionCleared ()
|
||||
{
|
||||
// if we have a clear pending, this could be the trigger that
|
||||
// allows us to clear our action
|
||||
maybeClearAction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues up code to be invoked when the action is completely cleared
|
||||
* (including all remaining interesting sprites and animations on the
|
||||
* puzzle board).
|
||||
*/
|
||||
protected void fireWhenActionCleared (ClearPender pender)
|
||||
{
|
||||
// if the action is already ended, fire this pender immediately
|
||||
if (_astate == ACTION_CLEARED) {
|
||||
if (pender.actionCleared() == ClearPender.RESTART_ACTION) {
|
||||
Log.debug("Restarting action at behest of pender " +
|
||||
pender + ".");
|
||||
startAction();
|
||||
}
|
||||
|
||||
} else {
|
||||
Log.debug("Queueing action pender " + pender + ".");
|
||||
_clearPenders.add(pender);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not it is safe to clear the action. The default
|
||||
* behavior is to not allow the action to be cleared until all
|
||||
* interesting sprites and animations in the board view have finished.
|
||||
* If derived classes or delegates wish to postpone the clearing of
|
||||
* the action, they can return false from this method, but they must
|
||||
* then be sure to call {@link #maybeClearAction} when whatever
|
||||
* condition that caused them to desire to postpone action clearing
|
||||
* has finally been satisfied.
|
||||
*/
|
||||
protected boolean canClearAction ()
|
||||
{
|
||||
final boolean[] canClear = new boolean[1];
|
||||
canClear[0] = (_pview.getActionCount() == 0);
|
||||
// if (!canClear[0]) {
|
||||
// _pview.dumpActors();
|
||||
// PuzzleBoardView.DEBUG_ACTION = true;
|
||||
// }
|
||||
|
||||
// let our delegates do their business
|
||||
applyToDelegates(PuzzleControllerDelegate.class, new DelegateOp() {
|
||||
public void apply (PlaceControllerDelegate delegate) {
|
||||
canClear[0] = canClear[0] &&
|
||||
((PuzzleControllerDelegate)delegate).canClearAction();
|
||||
}
|
||||
});
|
||||
|
||||
return canClear[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to effect the actual clearing of our action if we've
|
||||
* received some asynchronous trigger that indicates that it may well
|
||||
* be safe now to clear the action.
|
||||
*/
|
||||
protected void maybeClearAction ()
|
||||
{
|
||||
if (_astate == CLEAR_PENDING && canClearAction()) {
|
||||
actuallyClearAction();
|
||||
// } else {
|
||||
// Log.info("Not clearing action [astate=" + _astate +
|
||||
// ", canClear=" + canClearAction() + "].");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the actual process of clearing the action for this puzzle.
|
||||
* This is only called after it is known to be safe to clear the
|
||||
* action. Derived classes can override this method and clear out
|
||||
* anything that is not needed while the puzzle's "action" is not
|
||||
* going (timers, etc.). Anything that is cleared out here should be
|
||||
* recreated in {@link #startAction}.
|
||||
*/
|
||||
protected void actuallyClearAction ()
|
||||
{
|
||||
Log.debug("Actually clearing action.");
|
||||
|
||||
// make a note that we've cleared the action
|
||||
_astate = ACTION_CLEARED;
|
||||
// PuzzleBoardView.DEBUG_ACTION = false;
|
||||
|
||||
// let our delegates do their business
|
||||
applyToDelegates(PuzzleControllerDelegate.class, new DelegateOp() {
|
||||
public void apply (PlaceControllerDelegate delegate) {
|
||||
((PuzzleControllerDelegate)delegate).clearAction();
|
||||
}
|
||||
});
|
||||
|
||||
// let our panel know what's up
|
||||
_panel.clearAction();
|
||||
_panel.setPuzzleGrabsKeys(false); // let the user chat
|
||||
|
||||
// deliver one final update to the server
|
||||
sendProgressUpdate();
|
||||
|
||||
// let derived classes do things
|
||||
try {
|
||||
actionWasCleared();
|
||||
} catch (Exception e) {
|
||||
Log.warning("Choked in actionWasCleared");
|
||||
Log.logStackTrace(e);
|
||||
}
|
||||
|
||||
// notify any penders that the action has cleared
|
||||
final int[] results = new int[2];
|
||||
_clearPenders.apply(new ObserverList.ObserverOp() {
|
||||
public boolean apply (Object observer) {
|
||||
switch (((ClearPender)observer).actionCleared()) {
|
||||
case ClearPender.RESTART_ACTION: results[0]++; break;
|
||||
case ClearPender.NO_RESTART_ACTION: results[1]++; break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
_clearPenders.clear();
|
||||
|
||||
// if there are no refusals and at least one restart request, go
|
||||
// ahead and restart the action now
|
||||
if (results[1] == 0 && results[0] > 0) {
|
||||
startAction();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the action was actually cleared, but before the action
|
||||
* obsevers are notified.
|
||||
*/
|
||||
protected void actionWasCleared ()
|
||||
{
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public boolean handleAction (ActionEvent action)
|
||||
{
|
||||
String cmd = action.getActionCommand();
|
||||
if (cmd.equals(TOGGLE_CHATTING)) {
|
||||
setChatting(!isChatting());
|
||||
|
||||
} else {
|
||||
return super.handleAction(action);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the delay in milliseconds between sending each progress
|
||||
* update event to the server. Derived classes may wish to override
|
||||
* this to send their progress updates more or less frequently than
|
||||
* the default.
|
||||
*/
|
||||
protected long getProgressInterval ()
|
||||
{
|
||||
return DEFAULT_PROGRESS_INTERVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal the game to generate and distribute a new board.
|
||||
*/
|
||||
protected void generateNewBoard ()
|
||||
{
|
||||
// wait for any animations or sprites in the board to finish their
|
||||
// business before setting the board into place
|
||||
fireWhenActionCleared(new ClearPender() {
|
||||
public int actionCleared () {
|
||||
// update the player board
|
||||
_pboard = newBoard();
|
||||
if (_puzobj.seed != 0) {
|
||||
_pboard.initializeSeed(_puzobj.seed);
|
||||
}
|
||||
setBoard(_pboard);
|
||||
_pview.setBoard(_pboard);
|
||||
|
||||
// and repaint things
|
||||
_pview.repaint();
|
||||
|
||||
// let our delegates do their business
|
||||
DelegateOp dop = new DelegateOp() {
|
||||
public void apply (PlaceControllerDelegate delegate) {
|
||||
((PuzzleControllerDelegate)delegate).setBoard(_pboard);
|
||||
}
|
||||
};
|
||||
applyToDelegates(PuzzleControllerDelegate.class, dop);
|
||||
|
||||
return CARE_NOT;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of progress events currently queued up for
|
||||
* sending to the server with the next progress update.
|
||||
*/
|
||||
public int getEventCount ()
|
||||
{
|
||||
return _events.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Are we syncing boards for this puzzle?
|
||||
* By default, we defer to the PuzzlePanel and its runtime config.
|
||||
*/
|
||||
protected boolean isSyncingBoards ()
|
||||
{
|
||||
return PuzzlePanel.isSyncingBoards();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given progress event and a snapshot of the supplied board
|
||||
* state to the set of progress events and associated board states for
|
||||
* later transmission to the server.
|
||||
*/
|
||||
public void addProgressEvent (int event, Board board)
|
||||
{
|
||||
// make sure they don't queue things up at strange times
|
||||
if (_puzobj.state != PuzzleObject.IN_PLAY) {
|
||||
Log.warning("Rejecting progress event; game not in play " +
|
||||
"[puzobj=" + _puzobj.which() +
|
||||
", event=" + event + "].");
|
||||
return;
|
||||
}
|
||||
|
||||
_events.add(Integer.valueOf(event));
|
||||
if (isSyncingBoards()) {
|
||||
_states.add((board == null) ? null : board.clone());
|
||||
if (board == null) {
|
||||
Log.warning("Added progress event with no associated board " +
|
||||
"state, server will not be able to ensure " +
|
||||
"board state synchronization.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the server a game progress update with the list of events, as
|
||||
* well as board states if {@link PuzzlePanel#isSyncingBoards} is true.
|
||||
*/
|
||||
public void sendProgressUpdate ()
|
||||
{
|
||||
// make sure we have our puzzle object and events to send
|
||||
int size = _events.size();
|
||||
if (size == 0 || _puzobj == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// create an array of the events we're sending to the server
|
||||
int[] events = CollectionUtil.toIntArray(_events);
|
||||
_events.clear();
|
||||
|
||||
// Log.info("Sending progress [round=" + _puzobj.roundId +
|
||||
// ", events=" + StringUtil.toString(events) + "].");
|
||||
|
||||
// create an array of the board states that correspond with those
|
||||
// events (if state syncing is enabled)
|
||||
int numStates = _states.size();
|
||||
if (numStates == size) { // ie, if we have a board to match every event
|
||||
Board[] states = new Board[numStates];
|
||||
_states.toArray(states);
|
||||
_states.clear();
|
||||
|
||||
// send the update progress request
|
||||
_puzobj.puzzleGameService.updateProgressSync(
|
||||
_ctx.getClient(), _puzobj.roundId, events, states);
|
||||
|
||||
} else {
|
||||
// send the update progress request
|
||||
_puzobj.puzzleGameService.updateProgress(
|
||||
_ctx.getClient(), _puzobj.roundId, events);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a player is knocked out of the game to give the puzzle
|
||||
* a chance to perform any post-knockout actions that may be desired.
|
||||
* Derived classes may wish to override this method but should be sure
|
||||
* to call <code>super.playerKnockedOut()</code>.
|
||||
*/
|
||||
protected void playerKnockedOut (final int pidx)
|
||||
{
|
||||
// dispatch this to our delegates
|
||||
applyToDelegates(PuzzleControllerDelegate.class, new DelegateOp() {
|
||||
public void apply (PlaceControllerDelegate delegate) {
|
||||
((PuzzleControllerDelegate)delegate).playerKnockedOut(pidx);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Catches clicks an unpauses, without passing the click through
|
||||
* to the puzzle.
|
||||
*/
|
||||
class Unpauser extends MouseHijacker
|
||||
{
|
||||
public Unpauser (PuzzlePanel panel)
|
||||
{
|
||||
super(panel.getBoardView());
|
||||
_panel = panel;
|
||||
panel.addMouseListener(_clicker);
|
||||
panel.getBoardView().addMouseListener(_clicker);
|
||||
}
|
||||
|
||||
public Component release ()
|
||||
{
|
||||
_panel.removeMouseListener(_clicker);
|
||||
_panel.getBoardView().removeMouseListener(_clicker);
|
||||
return super.release();
|
||||
}
|
||||
|
||||
protected MouseAdapter _clicker = new MouseAdapter() {
|
||||
public void mousePressed (MouseEvent event) {
|
||||
setChatting(false); // this will call release
|
||||
}
|
||||
};
|
||||
|
||||
protected PuzzlePanel _panel;
|
||||
}
|
||||
|
||||
/** A special frame participant that handles the sending of puzzle
|
||||
* progress updates. We can't just
|
||||
* register an interval for this because sometimes the clock goes
|
||||
* backwards in time in windows and our intervals don't get called for
|
||||
* a long period of time which causes the server to think the client
|
||||
* is disconnected or cheating and resign them from the puzzle. God
|
||||
* bless you, Microsoft. */
|
||||
protected class Updater implements FrameParticipant
|
||||
{
|
||||
public void tick (long tickStamp) {
|
||||
if (_astate == ACTION_CLEARED) {
|
||||
// remove ourselves as the action is now cleared; we can't
|
||||
// do this in actuallyClearAction() because that might get
|
||||
// called during the PuzzlePanel's frame tick and it's
|
||||
// only safe to remove yourself during a tick(), not
|
||||
// another frame participant
|
||||
_pctx.getFrameManager().removeFrameParticipant(_updater);
|
||||
|
||||
} else if (tickStamp - _lastProgressTick > getProgressInterval()) {
|
||||
_lastProgressTick = tickStamp;
|
||||
sendProgressUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean needsPaint () {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Component getComponent () {
|
||||
return null;
|
||||
}
|
||||
|
||||
public long _lastProgressTick;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the updater to be used in this puzzle.
|
||||
*/
|
||||
protected Updater createUpdater ()
|
||||
{
|
||||
return new Updater();
|
||||
}
|
||||
|
||||
/** The mouse jockey for unpausing our puzzles. */
|
||||
protected Unpauser _unpauser;
|
||||
|
||||
/** Handles the sending of puzzle progress updates. */
|
||||
protected Updater _updater = createUpdater();
|
||||
|
||||
/** Listens for players being knocked out. */
|
||||
protected ElementUpdateListener _kolist = new ElementUpdateListener() {
|
||||
public void elementUpdated (ElementUpdatedEvent event) {
|
||||
String name = event.getName();
|
||||
if (name.equals(PuzzleObject.PLAYER_STATUS)) {
|
||||
if (event.getIntValue() == GameObject.PLAYER_LEFT_GAME) {
|
||||
playerKnockedOut(event.getIndex());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** Listens for various attribute changes. */
|
||||
protected AttributeChangeListener _mlist = new AttributeChangeListener() {
|
||||
public void attributeChanged (AttributeChangedEvent event) {
|
||||
String name = event.getName();
|
||||
if (name.equals(PuzzleObject.SEED)) {
|
||||
generateNewBoard();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** A casted reference to the client context. */
|
||||
protected PuzzleContext _pctx;
|
||||
|
||||
/** Our player index in the game. */
|
||||
protected int _pidx;
|
||||
|
||||
/** The puzzle panel. */
|
||||
protected PuzzlePanel _panel;
|
||||
|
||||
/** The puzzle config. */
|
||||
protected PuzzleConfig _puzconfig;
|
||||
|
||||
/** A reference to our puzzle game object. */
|
||||
protected PuzzleObject _puzobj;
|
||||
|
||||
/** The puzzle board view. */
|
||||
protected PuzzleBoardView _pview;
|
||||
|
||||
/** The puzzle board data. */
|
||||
protected Board _pboard;
|
||||
|
||||
/** The list of relevant game events since the last progress update. */
|
||||
protected ArrayList _events = new ArrayList();
|
||||
|
||||
/** Board snapshots that correspond to our board state after each of
|
||||
* our events has been applied. */
|
||||
protected ArrayList _states = new ArrayList();
|
||||
|
||||
/** A flag indicating that we're in chatting mode. */
|
||||
protected boolean _chatting = false;
|
||||
|
||||
/** The current action state of the puzzle. */
|
||||
protected int _astate = ACTION_CLEARED;
|
||||
|
||||
/** The action cleared penders. */
|
||||
protected ObserverList _clearPenders = new ObserverList(
|
||||
ObserverList.SAFE_IN_ORDER_NOTIFY);
|
||||
|
||||
/** A key listener that currently just toggles pause in the puzzle. */
|
||||
protected KeyListener _globalKeyListener = new KeyAdapter() {
|
||||
public void keyReleased (KeyEvent e)
|
||||
{
|
||||
int keycode = e.getKeyCode();
|
||||
// toggle chatting (pause)
|
||||
if (keycode == KeyEvent.VK_ESCAPE || keycode == KeyEvent.VK_PAUSE) {
|
||||
setChatting(!isChatting());
|
||||
|
||||
// pressing P also to pause (but not unpause),
|
||||
// and only if it has not been reassigned
|
||||
} else if (keycode == KeyEvent.VK_P && !isChatting() &&
|
||||
!_panel._xlate.hasCommand(KeyEvent.VK_P)) {
|
||||
setChatting(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** The delay in milliseconds between progress update intervals. */
|
||||
protected static final long DEFAULT_PROGRESS_INTERVAL = 6000L;
|
||||
|
||||
/** A {@link #_astate} constant. */
|
||||
protected static final int ACTION_CLEARED = 0;
|
||||
|
||||
/** A {@link #_astate} constant. */
|
||||
protected static final int CLEAR_PENDING = 1;
|
||||
|
||||
/** A {@link #_astate} constant. */
|
||||
protected static final int ACTION_GOING = 2;
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
//
|
||||
// $Id: PuzzleControllerDelegate.java 3381 2005-03-03 19:36:34Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.client;
|
||||
|
||||
import com.threerings.crowd.data.PlaceObject;
|
||||
|
||||
import com.threerings.parlor.game.client.GameControllerDelegate;
|
||||
|
||||
import com.threerings.puzzle.data.Board;
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
/** Our puzzle controller. */
|
||||
protected PuzzleController _ctrl;
|
||||
|
||||
/** The puzzle distributed object. */
|
||||
protected PuzzleObject _puzobj;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// $Id: PuzzleGameService.java 4145 2006-05-24 01:24:24Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
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 PuzzlePanel#isSyncingBoards} 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,319 @@
|
||||
//
|
||||
// $Id: PuzzlePanel.java 4007 2006-04-10 08:59:30Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.client;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import com.samskivert.swing.Controller;
|
||||
import com.samskivert.swing.ControllerProvider;
|
||||
import com.samskivert.swing.RuntimeAdjust;
|
||||
import com.samskivert.swing.util.SwingUtil;
|
||||
|
||||
import com.threerings.util.KeyTranslator;
|
||||
|
||||
import com.threerings.crowd.client.PlaceView;
|
||||
import com.threerings.crowd.data.PlaceObject;
|
||||
|
||||
import com.threerings.parlor.util.RobotPlayer;
|
||||
|
||||
import com.threerings.puzzle.Log;
|
||||
import com.threerings.puzzle.data.PuzzleCodes;
|
||||
import com.threerings.puzzle.data.PuzzleConfig;
|
||||
import com.threerings.puzzle.data.PuzzleGameCodes;
|
||||
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);
|
||||
|
||||
// configure the puzzle panel
|
||||
setLayout(new BorderLayout());
|
||||
|
||||
// create the puzzle board view
|
||||
_bview = createBoardView(ctx);
|
||||
|
||||
// create the puzzle board panel
|
||||
_bpanel = createBoardPanel(ctx);
|
||||
|
||||
// add the board panel
|
||||
add(_bpanel, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void addNotify ()
|
||||
{
|
||||
super.addNotify();
|
||||
|
||||
// leave the keyboard manager disabled to start, and set things up
|
||||
// for chatting
|
||||
setPuzzleGrabsKeys(false);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void removeNotify ()
|
||||
{
|
||||
super.removeNotify();
|
||||
|
||||
// don't ever leave with the keys grabbed
|
||||
setPuzzleGrabsKeys(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if an overlay panel is currently being displayed.
|
||||
*/
|
||||
public boolean hasOverlay ()
|
||||
{
|
||||
return _opanel != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = createRobotPlayer();
|
||||
}
|
||||
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().requestFocusInWindow();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the controller when the action starts.
|
||||
*/
|
||||
public void startAction ()
|
||||
{
|
||||
// make the first player a robot player
|
||||
|
||||
if (isRobotTesting() && _controller.getPlayerIndex() == 0) {
|
||||
setRobotPlayer(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the controller when the action stops.
|
||||
*/
|
||||
public void clearAction ()
|
||||
{
|
||||
// deactivate the robot player
|
||||
if (isRobotTesting() && _controller.getPlayerIndex() == 0) {
|
||||
setRobotPlayer(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a robot player using the default RobotPlayer. Derived
|
||||
* classes can override to make use of more advanced robots adapted to
|
||||
* specific puzzles.
|
||||
*/
|
||||
protected RobotPlayer createRobotPlayer ()
|
||||
{
|
||||
return new RobotPlayer(this, _xlate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (PuzzleContext ctx);
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/** Returns true if the robot tester is activated. */
|
||||
public static boolean isRobotTesting ()
|
||||
{
|
||||
return _robotTest.getValue();
|
||||
}
|
||||
|
||||
/** Returns true if board syncing is activated. */
|
||||
public static boolean isSyncingBoards ()
|
||||
{
|
||||
return _syncBoardState.getValue();
|
||||
}
|
||||
|
||||
/** 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;
|
||||
|
||||
/** A debug hook that toggles activation of the robot test player. */
|
||||
protected static RuntimeAdjust.BooleanAdjust _robotTest =
|
||||
new RuntimeAdjust.BooleanAdjust(
|
||||
"Activates the robot test player which will make random moves " +
|
||||
"in any activated puzzle.", "narya.puzzle.robot_tester",
|
||||
PuzzlePrefs.config, false);
|
||||
|
||||
/** A debug hook that toggles activation of board syncing. */
|
||||
protected static RuntimeAdjust.BooleanAdjust _syncBoardState =
|
||||
new RuntimeAdjust.BooleanAdjust(
|
||||
"Sends a snapshot of the puzzle board with every event to aid " +
|
||||
"in debugging.", "narya.puzzle.sync_board_state",
|
||||
PuzzlePrefs.config, false);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// $Id: PuzzlePrefs.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.client;
|
||||
|
||||
import com.samskivert.util.Config;
|
||||
|
||||
/**
|
||||
* Provides access to runtime configuration parameters for this package
|
||||
* and its subpackages.
|
||||
*/
|
||||
public class PuzzlePrefs
|
||||
{
|
||||
/** Used to load our preferences from a properties file and map them
|
||||
* to the persistent Java preferences repository. */
|
||||
public static Config config = new Config("rsrc/config/puzzle");
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
//
|
||||
// $Id: Board.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.data;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import com.threerings.io.Streamable;
|
||||
|
||||
/**
|
||||
* An abstract base class for generating and storing puzzle board data.
|
||||
*/
|
||||
public abstract class Board
|
||||
implements Cloneable, Streamable
|
||||
{
|
||||
/**
|
||||
* Outputs a string representation of the board contents.
|
||||
*/
|
||||
public abstract void dump ();
|
||||
|
||||
/**
|
||||
* Outputs a string representation of the board contents, interlaced
|
||||
* with the supplied comparison board.
|
||||
*/
|
||||
public abstract void dumpAndCompare (Board other);
|
||||
|
||||
/**
|
||||
* Returns whether this board is equal to the given comparison board.
|
||||
*/
|
||||
public abstract boolean equals (Board other);
|
||||
|
||||
// documentation inherited
|
||||
public Object clone ()
|
||||
{
|
||||
try {
|
||||
Board board = (Board)super.clone();
|
||||
board._rando = (BoardRandom)_rando.clone();
|
||||
return board;
|
||||
} catch (CloneNotSupportedException cnse) {
|
||||
throw new RuntimeException("All is wrong with universe.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the seed in the board's random number generator and calls
|
||||
* {@link #populate}.
|
||||
*/
|
||||
public void initializeSeed (long seed)
|
||||
{
|
||||
_rando = new BoardRandom(seed);
|
||||
populate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the random number generator used by the board to generate
|
||||
* random numbers for our puzzles.
|
||||
*/
|
||||
public Random getRandom ()
|
||||
{
|
||||
return _rando;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after the seed is set in the board to give derived classes a
|
||||
* chance to do things like populating the board with random pieces.
|
||||
*/
|
||||
protected void populate ()
|
||||
{
|
||||
}
|
||||
|
||||
/** Used to generate random numbers. */
|
||||
protected static class BoardRandom extends Random
|
||||
implements Cloneable
|
||||
{
|
||||
public BoardRandom (long seed)
|
||||
{
|
||||
super(0L);
|
||||
setSeed(seed);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public synchronized void setSeed (long seed)
|
||||
{
|
||||
_seed = (seed ^ multiplier) & mask;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
synchronized protected int next (int bits)
|
||||
{
|
||||
long nextseed = (_seed * multiplier + addend) & mask;
|
||||
_seed = nextseed;
|
||||
return (int)(nextseed >>> (48 - bits));
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void nextBytes (byte[] bytes)
|
||||
{
|
||||
unimplemented();
|
||||
}
|
||||
|
||||
// not overridden: (They seemed innocent enough)
|
||||
// nextInt()
|
||||
// nextLong()
|
||||
// nextBoolean()
|
||||
// nextFloat()
|
||||
|
||||
// documentation inherited
|
||||
public int nextInt (int n)
|
||||
{
|
||||
if (n <= 0) {
|
||||
throw new IllegalArgumentException("n must be positive");
|
||||
}
|
||||
|
||||
if ((n & -n) == n) { // i.e., n is a power of 2
|
||||
return (int)((n * (long)next(31)) >> 31);
|
||||
}
|
||||
|
||||
int bits, val;
|
||||
do {
|
||||
bits = next(31);
|
||||
val = bits % n;
|
||||
} while (bits - val + (n-1) < 0);
|
||||
return val;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public double nextDouble ()
|
||||
{
|
||||
long l = ((long)(next(26)) << 27) + next(27);
|
||||
return l / (double)(1L << 53);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public synchronized double nextGaussian ()
|
||||
{
|
||||
if (_haveNextNextGaussian) {
|
||||
_haveNextNextGaussian = false;
|
||||
return _nextNextGaussian;
|
||||
|
||||
} else {
|
||||
double v1, v2, s;
|
||||
do {
|
||||
v1 = 2 * nextDouble() - 1;
|
||||
v2 = 2 * nextDouble() - 1;
|
||||
s = v1 * v1 + v2 * v2;
|
||||
} while (s >= 1 || s == 0);
|
||||
double multiplier = Math.sqrt(-2 * Math.log(s)/s);
|
||||
_nextNextGaussian = v2 * multiplier;
|
||||
_haveNextNextGaussian = true;
|
||||
return v1 * multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public Object clone ()
|
||||
{
|
||||
try {
|
||||
return super.clone();
|
||||
} catch (CloneNotSupportedException cnse) {
|
||||
throw new RuntimeException("All is wrong with universe.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* I suppose I could copy all the methods from Random, then we
|
||||
* wouldn't need this..
|
||||
*/
|
||||
private final void unimplemented ()
|
||||
{
|
||||
throw new RuntimeException(
|
||||
"The Random method you attempted to call " +
|
||||
"has not been implemented by BoardRandom.");
|
||||
}
|
||||
|
||||
/** The internal state related to generating random numbers. */
|
||||
protected long _seed;
|
||||
protected double _nextNextGaussian;
|
||||
protected boolean _haveNextNextGaussian = false;
|
||||
|
||||
private final static long multiplier = 0x5DEECE66DL;
|
||||
private final static long addend = 0xBL;
|
||||
private final static long mask = (1L << 48) - 1;
|
||||
}
|
||||
|
||||
/** The object we use to generate our random numbers. */
|
||||
protected transient BoardRandom _rando;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// $Id: BoardSummary.java 3726 2005-10-11 19:17:43Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.data;
|
||||
|
||||
import com.threerings.io.SimpleStreamableObject;
|
||||
|
||||
/**
|
||||
* Provides summarized data representing a player's board in a puzzle
|
||||
* game. Board summaries are maintained by the puzzle server and are
|
||||
* periodically sent to the clients to give them a view into how well
|
||||
* their opponent(s) are doing. The data required to marshal a board
|
||||
* summary object should be notably smaller in size than what would be
|
||||
* required to marshal the entire associated {@link Board}.
|
||||
*
|
||||
* <p> Note all non-transient members of this and derived classes will
|
||||
* automatically be serialized when the summary is sent over the wire.
|
||||
*/
|
||||
public abstract class BoardSummary extends SimpleStreamableObject
|
||||
{
|
||||
/**
|
||||
* Constructs an empty board summary for use when un-serializing.
|
||||
*/
|
||||
public BoardSummary ()
|
||||
{
|
||||
// nothing for now
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a board summary that retrieves full board information
|
||||
* from the supplied board when summarizing.
|
||||
*/
|
||||
public BoardSummary (Board board)
|
||||
{
|
||||
setBoard(board);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the board associated with this board summary, causing
|
||||
* an immediate update to this summary.
|
||||
*/
|
||||
public void setBoard (Board board)
|
||||
{
|
||||
_board = board;
|
||||
summarize(); // immediately summarize the new board
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the {@link
|
||||
* com.threerings.puzzle.server.PuzzleManager} to refresh the
|
||||
* board summary information by studying the associated board
|
||||
* contents.
|
||||
*/
|
||||
public abstract void summarize ();
|
||||
|
||||
/** The board that we're summarizing. This is only valid on the
|
||||
* server, and on the client only for the actual player's board. */
|
||||
protected transient Board _board;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// $Id: PuzzleCodes.java 3640 2005-07-01 23:56:54Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.data;
|
||||
|
||||
import com.threerings.presents.data.InvocationCodes;
|
||||
|
||||
/**
|
||||
* Constants relating to the puzzle services.
|
||||
*/
|
||||
public interface PuzzleCodes extends InvocationCodes
|
||||
{
|
||||
/** The message bundle identifier for general puzzle messages. */
|
||||
public static final String PUZZLE_MESSAGE_BUNDLE = "puzzle.general";
|
||||
|
||||
/** The default puzzle difficulty level. */
|
||||
public static final int DEFAULT_DIFFICULTY = 2;
|
||||
|
||||
/** Whether to enable debug logging and assertions for puzzles. Note
|
||||
* that enabling this may result in the server or client exiting
|
||||
* unexpectedly if certain error conditions arise in order to
|
||||
* facilitate debugging, and so this should never be enabled in any
|
||||
* environment even remotely resembling production. */
|
||||
public static final boolean DEBUG_PUZZLE = false;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// $Id: PuzzleConfig.java 3381 2005-03-03 19:36:34Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.data;
|
||||
|
||||
import com.threerings.parlor.game.data.GameConfig;
|
||||
|
||||
/**
|
||||
* Encapsulates the basic configuration information for a puzzle game.
|
||||
*/
|
||||
public abstract class PuzzleConfig extends GameConfig
|
||||
implements Cloneable
|
||||
{
|
||||
/**
|
||||
* Constructs a blank puzzle config.
|
||||
*/
|
||||
public PuzzleConfig ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message bundle identifier for the bundle that should be
|
||||
* used to translate the translatable strings used to describe the
|
||||
* puzzle config parameters. The default implementation returns the
|
||||
* base puzzle message bundle, but puzzles that have their own message
|
||||
* bundle should override this method and return their puzzle-specific
|
||||
* bundle identifier.
|
||||
*/
|
||||
public String getBundleName ()
|
||||
{
|
||||
return PuzzleCodes.PUZZLE_MESSAGE_BUNDLE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// $Id: PuzzleGameCodes.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.data;
|
||||
|
||||
/**
|
||||
* Contains codes used by the puzzle game client implementations. This
|
||||
* differs from {@link PuzzleCodes} as that is related to the puzzle
|
||||
* services which span the client and the server.
|
||||
*/
|
||||
public interface PuzzleGameCodes
|
||||
{
|
||||
// Nothing at the moment.
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// $Id: PuzzleGameMarshaller.java 4145 2006-05-24 01:24:24Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2006 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.data;
|
||||
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.data.InvocationMarshaller;
|
||||
import com.threerings.presents.dobj.InvocationResponseEvent;
|
||||
import com.threerings.puzzle.client.PuzzleGameService;
|
||||
import com.threerings.puzzle.data.Board;
|
||||
|
||||
/**
|
||||
* Provides the implementation of the {@link PuzzleGameService} interface
|
||||
* that marshalls the arguments and delivers the request to the provider
|
||||
* on the server. Also provides an implementation of the response listener
|
||||
* interfaces that marshall the response arguments and deliver them back
|
||||
* to the requesting client.
|
||||
*/
|
||||
public class PuzzleGameMarshaller extends InvocationMarshaller
|
||||
implements PuzzleGameService
|
||||
{
|
||||
/** The method id used to dispatch {@link #updateProgress} requests. */
|
||||
public static final int UPDATE_PROGRESS = 1;
|
||||
|
||||
// documentation inherited from interface
|
||||
public void updateProgress (Client arg1, int arg2, int[] arg3)
|
||||
{
|
||||
sendRequest(arg1, UPDATE_PROGRESS, new Object[] {
|
||||
Integer.valueOf(arg2), arg3
|
||||
});
|
||||
}
|
||||
|
||||
/** The method id used to dispatch {@link #updateProgressSync} requests. */
|
||||
public static final int UPDATE_PROGRESS_SYNC = 2;
|
||||
|
||||
// documentation inherited from interface
|
||||
public void updateProgressSync (Client arg1, int arg2, int[] arg3, Board[] arg4)
|
||||
{
|
||||
sendRequest(arg1, UPDATE_PROGRESS_SYNC, new Object[] {
|
||||
Integer.valueOf(arg2), arg3, arg4
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
//
|
||||
// $Id: PuzzleObject.java 4145 2006-05-24 01:24:24Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.data;
|
||||
|
||||
import com.threerings.parlor.game.data.GameObject;
|
||||
|
||||
/**
|
||||
* Extends the basic {@link GameObject} to add individual player
|
||||
* status. Puzzle games typically contain numerous players that may be
|
||||
* knocked out of the game while the overall game continues on, thereby
|
||||
* necessitating this second level of game status.
|
||||
*/
|
||||
public class PuzzleObject extends GameObject
|
||||
implements PuzzleCodes
|
||||
{
|
||||
// AUTO-GENERATED: FIELDS START
|
||||
/** The field name of the <code>puzzleGameService</code> field. */
|
||||
public static final String PUZZLE_GAME_SERVICE = "puzzleGameService";
|
||||
|
||||
/** The field name of the <code>difficulty</code> field. */
|
||||
public static final String DIFFICULTY = "difficulty";
|
||||
|
||||
/** The field name of the <code>summaries</code> field. */
|
||||
public static final String SUMMARIES = "summaries";
|
||||
|
||||
/** The field name of the <code>seed</code> field. */
|
||||
public static final String SEED = "seed";
|
||||
// AUTO-GENERATED: FIELDS END
|
||||
|
||||
/** Provides general puzzle game invocation services. */
|
||||
public PuzzleGameMarshaller puzzleGameService;
|
||||
|
||||
/** The puzzle difficulty level. */
|
||||
public int difficulty;
|
||||
|
||||
/** Summaries of the boards of all players in this puzzle (may be null
|
||||
* if the puzzle doesn't support individual player boards). */
|
||||
public BoardSummary[] summaries;
|
||||
|
||||
/** The seed used to germinate the boards. */
|
||||
public long seed;
|
||||
|
||||
// AUTO-GENERATED: METHODS START
|
||||
/**
|
||||
* Requests that the <code>puzzleGameService</code> 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 setPuzzleGameService (PuzzleGameMarshaller value)
|
||||
{
|
||||
PuzzleGameMarshaller ovalue = this.puzzleGameService;
|
||||
requestAttributeChange(
|
||||
PUZZLE_GAME_SERVICE, value, ovalue);
|
||||
this.puzzleGameService = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the <code>difficulty</code> 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 setDifficulty (int value)
|
||||
{
|
||||
int ovalue = this.difficulty;
|
||||
requestAttributeChange(
|
||||
DIFFICULTY, Integer.valueOf(value), Integer.valueOf(ovalue));
|
||||
this.difficulty = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the <code>summaries</code> 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 setSummaries (BoardSummary[] value)
|
||||
{
|
||||
BoardSummary[] ovalue = this.summaries;
|
||||
requestAttributeChange(
|
||||
SUMMARIES, value, ovalue);
|
||||
this.summaries = (value == null) ? null : (BoardSummary[])value.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the <code>index</code>th element of
|
||||
* <code>summaries</code> 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 setSummariesAt (BoardSummary value, int index)
|
||||
{
|
||||
BoardSummary ovalue = this.summaries[index];
|
||||
requestElementUpdate(
|
||||
SUMMARIES, index, value, ovalue);
|
||||
this.summaries[index] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the <code>seed</code> 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 setSeed (long value)
|
||||
{
|
||||
long ovalue = this.seed;
|
||||
requestAttributeChange(
|
||||
SEED, Long.valueOf(value), Long.valueOf(ovalue));
|
||||
this.seed = value;
|
||||
}
|
||||
// AUTO-GENERATED: METHODS END
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
//
|
||||
// $Id: DropBlockSprite.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.drop.client;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
|
||||
/**
|
||||
* The drop block sprite represents a block of multiple pieces that can be
|
||||
* rotated to any of the four cardinal compass directions. As such, it
|
||||
* may span multiple columns or rows depending on its orientation. The
|
||||
* block has a "central" piece around which it rotates, with the other
|
||||
* pieces referred to as "external" pieces.
|
||||
*/
|
||||
public class DropBlockSprite extends DropSprite
|
||||
{
|
||||
/**
|
||||
* Constructs a drop block sprite and starts it dropping.
|
||||
*
|
||||
* @param view the board view upon which this sprite will be displayed.
|
||||
* @param col the column of the central piece.
|
||||
* @param row the row of the central piece.
|
||||
* @param orient the orientation of the sprite.
|
||||
* @param pieces the pieces displayed by the sprite.
|
||||
*/
|
||||
public DropBlockSprite (
|
||||
DropBoardView view, int col, int row, int orient, int[] pieces)
|
||||
{
|
||||
super(view, col, row, pieces, 0);
|
||||
|
||||
_orient = orient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a drop block sprite and starts it dropping.
|
||||
*
|
||||
* @param view the board view upon which this sprite will be displayed.
|
||||
* @param col the column of the central piece.
|
||||
* @param row the row of the central piece.
|
||||
* @param orient the orientation of the sprite.
|
||||
* @param pieces the pieces displayed by the sprite.
|
||||
* @param renderOrder the rendering order of the sprite.
|
||||
*/
|
||||
public DropBlockSprite (
|
||||
DropBoardView view, int col, int row, int orient, int[] pieces,
|
||||
int renderOrder)
|
||||
{
|
||||
super(view, col, row, pieces, 0, renderOrder);
|
||||
|
||||
_orient = orient;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void init ()
|
||||
{
|
||||
super.init();
|
||||
|
||||
setOrientation(_orient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of the row numbers containing the block pieces.
|
||||
* The first index is the row of the central piece. The array is
|
||||
* cached and re-used internally and so the caller should make their
|
||||
* own copy if they care to modify it.
|
||||
*/
|
||||
public int[] getRows ()
|
||||
{
|
||||
return _rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of the column numbers containing the block pieces.
|
||||
* The first index is the column of the central piece. The array is
|
||||
* cached and re-used internally and so the caller should make their
|
||||
* own copy if they care to modify it.
|
||||
*/
|
||||
public int[] getColumns ()
|
||||
{
|
||||
return _cols;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bounds of the block in board piece coordinates. The
|
||||
* bounds rectangle is cached and re-used internally and so the caller
|
||||
* should make their own copy if they care to modify it.
|
||||
*/
|
||||
public Rectangle getBoardBounds ()
|
||||
{
|
||||
return _dbounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the row the external piece is located in.
|
||||
*/
|
||||
public int getExternalRow ()
|
||||
{
|
||||
return _erow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the column the external piece is located in.
|
||||
*/
|
||||
public int getExternalColumn ()
|
||||
{
|
||||
return _ecol;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void setColumn (int col)
|
||||
{
|
||||
super.setColumn(col);
|
||||
updateDropInfo();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void setRow (int row)
|
||||
{
|
||||
super.setRow(row);
|
||||
updateDropInfo();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void setBoardLocation (int row, int col)
|
||||
{
|
||||
super.setBoardLocation(row, col);
|
||||
updateDropInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the sprite image offset to reflect the direction in which
|
||||
* the external piece is hanging.
|
||||
*/
|
||||
public void setOrientation (int orient)
|
||||
{
|
||||
super.setOrientation(orient);
|
||||
|
||||
int edx = 0, edy = 0;
|
||||
if (orient == NORTH) {
|
||||
edy = -1;
|
||||
} else if (orient == WEST) {
|
||||
edx = -1;
|
||||
}
|
||||
|
||||
// update the sprite image offset
|
||||
setRowOffset(edy);
|
||||
setColumnOffset(edx);
|
||||
|
||||
// update the external piece position and drop block bounds
|
||||
updateDropInfo();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void toString (StringBuilder buf)
|
||||
{
|
||||
super.toString(buf);
|
||||
buf.append(", erow=").append(_erow);
|
||||
buf.append(", ecol=").append(_ecol);
|
||||
}
|
||||
|
||||
/**
|
||||
* Can this sprite pop-up a row on a forgiving rotation?
|
||||
*/
|
||||
public boolean canPopup ()
|
||||
{
|
||||
return (_popups > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called if we pop up to decrement the remaining popups we have.
|
||||
*/
|
||||
public void didPopup ()
|
||||
{
|
||||
_popups--;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-calculates the external piece position and bounds of the drop
|
||||
* block.
|
||||
*/
|
||||
protected void updateDropInfo ()
|
||||
{
|
||||
// update the external piece location
|
||||
_erow = calculateExternalRow();
|
||||
_ecol = calculateExternalColumn();
|
||||
|
||||
// update the piece row and column arrays
|
||||
_rows[0] = _row;
|
||||
_rows[1] = _erow;
|
||||
_cols[0] = _col;
|
||||
_cols[1] = _ecol;
|
||||
|
||||
// calculate the drop block board bounds
|
||||
int maxrow = Math.max(_row, _erow);
|
||||
int mincol = Math.min(_col, _ecol);
|
||||
|
||||
int bpwid, bphei;
|
||||
if (_orient == NORTH || _orient == SOUTH) {
|
||||
bpwid = 1;
|
||||
bphei = 2;
|
||||
} else {
|
||||
bpwid = 2;
|
||||
bphei = 1;
|
||||
}
|
||||
|
||||
// create the bounds rectangle if necessary
|
||||
_dbounds.setBounds(mincol, maxrow, bpwid, bphei);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the row the external piece is located in based on the
|
||||
* current central piece location and sprite orientation.
|
||||
*/
|
||||
protected int calculateExternalRow ()
|
||||
{
|
||||
if (_orient == NORTH) {
|
||||
return (_row - 1);
|
||||
} else if (_orient == SOUTH) {
|
||||
return (_row + 1);
|
||||
} else {
|
||||
return _row;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the column the external piece is located in based on the
|
||||
* current central piece location and sprite orientation.
|
||||
*/
|
||||
protected int calculateExternalColumn ()
|
||||
{
|
||||
if (_orient == WEST) {
|
||||
return (_col - 1);
|
||||
} else if (_orient == EAST) {
|
||||
return (_col + 1);
|
||||
} else {
|
||||
return _col;
|
||||
}
|
||||
}
|
||||
|
||||
/** How many times this sprite can be popped-up a row in a forgiving
|
||||
* rotation. */
|
||||
protected byte _popups = 2;
|
||||
|
||||
/** The drop block bounds in board coordinates. */
|
||||
protected Rectangle _dbounds = new Rectangle();
|
||||
|
||||
/** The drop block piece rows. */
|
||||
protected int[] _rows = new int[2];
|
||||
|
||||
/** The drop block piece columns. */
|
||||
protected int[] _cols = new int[2];
|
||||
|
||||
/** The external piece location in board coordinates. */
|
||||
protected int _ecol, _erow;
|
||||
}
|
||||
@@ -0,0 +1,585 @@
|
||||
//
|
||||
// $Id: DropBoardView.java 3527 2005-04-28 23:17:29Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.drop.client;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import com.threerings.media.image.Mirage;
|
||||
import com.threerings.media.sprite.ImageSprite;
|
||||
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.parlor.media.ScoreAnimation;
|
||||
|
||||
import com.threerings.puzzle.Log;
|
||||
import com.threerings.puzzle.client.PuzzleBoardView;
|
||||
import com.threerings.puzzle.data.Board;
|
||||
import com.threerings.puzzle.data.PuzzleConfig;
|
||||
import com.threerings.puzzle.util.PuzzleContext;
|
||||
|
||||
import com.threerings.puzzle.drop.data.DropBoard;
|
||||
import com.threerings.puzzle.drop.data.DropConfig;
|
||||
import com.threerings.puzzle.drop.data.DropPieceCodes;
|
||||
|
||||
/**
|
||||
* The drop board view displays a drop puzzle game in progress for a
|
||||
* single player.
|
||||
*/
|
||||
public abstract class DropBoardView extends PuzzleBoardView
|
||||
implements DropPieceCodes
|
||||
{
|
||||
/** The color used to render normal scoring text. */
|
||||
public static final Color SCORE_COLOR = Color.white;
|
||||
|
||||
/** The color used to render chain reward scoring text. */
|
||||
public static final Color CHAIN_COLOR = Color.yellow;
|
||||
|
||||
/**
|
||||
* Constructs a drop board view.
|
||||
*/
|
||||
public DropBoardView (PuzzleContext ctx, int pwid, int phei)
|
||||
{
|
||||
super(ctx);
|
||||
|
||||
// save off piece dimensions
|
||||
_pwid = pwid;
|
||||
_phei = phei;
|
||||
|
||||
// determine distance to float score animations
|
||||
_scoreDist = 2 * _phei;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the board with the board dimensions.
|
||||
*/
|
||||
public void init (PuzzleConfig config)
|
||||
{
|
||||
DropConfig dconfig = (DropConfig)config;
|
||||
|
||||
// save off the board dimensions in pieces
|
||||
_bwid = dconfig.getBoardWidth();
|
||||
_bhei = dconfig.getBoardHeight();
|
||||
|
||||
super.init(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width in pixels of a single board piece.
|
||||
*/
|
||||
public int getPieceWidth ()
|
||||
{
|
||||
return _pwid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the height in pixels of a single board piece.
|
||||
*/
|
||||
public int getPieceHeight ()
|
||||
{
|
||||
return _phei;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the {@link DropSprite} to populate <code>pos</code> with
|
||||
* the screen coordinates in pixels at which a piece at <code>(col,
|
||||
* row)</code> in the board should be drawn. Derived classes may wish
|
||||
* to override this method to allow specialised positioning of
|
||||
* sprites.
|
||||
*/
|
||||
public void getPiecePosition (int col, int row, Point pos)
|
||||
{
|
||||
pos.setLocation(col * _pwid, (row * _phei) - _roff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the {@link DropSprite} to get the dimensions of the area
|
||||
* that will be occupied by rendering a piece segment of the given
|
||||
* orientation and length whose bottom-leftmost corner is at
|
||||
* <code>(col, row)</code>.
|
||||
*/
|
||||
public Dimension getPieceSegmentSize (int col, int row, int orient, int len)
|
||||
{
|
||||
if (orient == NORTH || orient == SOUTH) {
|
||||
return new Dimension(_pwid, len * _phei);
|
||||
} else {
|
||||
return new Dimension(len * _pwid, _phei);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new piece sprite and places it directly in it's correct
|
||||
* position.
|
||||
*/
|
||||
public void createPiece (int piece, int sx, int sy)
|
||||
{
|
||||
if (sx < 0 || sy < 0 || sx >= _bwid || sy >= _bhei) {
|
||||
Log.warning("Requested to create piece in invalid location " +
|
||||
"[sx=" + sx + ", sy=" + sy + "].");
|
||||
Thread.dumpStack();
|
||||
return;
|
||||
}
|
||||
createPiece(piece, sx, sy, sx, sy, 0L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the piece sprite at the specified location, if no sprite
|
||||
* exists at the location, one will be created. <em>Note:</em> this
|
||||
* method assumes the default {@link ImageSprite} is being used to
|
||||
* display pieces. If {@link #createPieceSprite} is overridden to
|
||||
* return a non-ImageSprite, this method must also be customized.
|
||||
*/
|
||||
public void updatePiece (int sx, int sy)
|
||||
{
|
||||
updatePiece(_dboard.getPiece(sx, sy), sx, sy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the piece sprite at the specified location, if no sprite
|
||||
* exists at the location, one will be created. <em>Note:</em> this
|
||||
* method assumes the default {@link ImageSprite} is being used to
|
||||
* display pieces. If {@link #createPieceSprite} is overridden to
|
||||
* return a non-ImageSprite, this method must also be customized.
|
||||
*/
|
||||
public void updatePiece (int piece, int sx, int sy)
|
||||
{
|
||||
if (sx < 0 || sy < 0 || sx >= _bwid || sy >= _bhei) {
|
||||
Log.warning("Requested to update piece in invalid location " +
|
||||
"[sx=" + sx + ", sy=" + sy + "].");
|
||||
Thread.dumpStack();
|
||||
return;
|
||||
}
|
||||
int spos = sy * _bwid + sx;
|
||||
if (_pieces[spos] != null) {
|
||||
((ImageSprite)_pieces[spos]).setMirage(
|
||||
getPieceImage(piece, sx, sy, NORTH));
|
||||
} else {
|
||||
createPiece(piece, sx, sy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new piece sprite and moves it into position on the board.
|
||||
*/
|
||||
public void createPiece (int piece, int sx, int sy, int tx, int ty,
|
||||
long duration)
|
||||
{
|
||||
if (tx < 0 || ty < 0 || tx >= _bwid || ty >= _bhei) {
|
||||
Log.warning("Requested to create and move piece to invalid " +
|
||||
"location [tx=" + tx + ", ty=" + ty + "].");
|
||||
Thread.dumpStack();
|
||||
return;
|
||||
}
|
||||
Sprite sprite = createPieceSprite(piece, sx, sy);
|
||||
if (sprite != null) {
|
||||
// position the piece properly to start
|
||||
Point start = new Point();
|
||||
getPiecePosition(sx, sy, start);
|
||||
sprite.setLocation(start.x, start.y);
|
||||
// now add it to the view
|
||||
addSprite(sprite);
|
||||
// and potentially move it into place
|
||||
movePiece(sprite, sx, sy, tx, ty, duration);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs the view to move the piece at the specified starting
|
||||
* position to the specified destination position. There must be a
|
||||
* sprite at the starting position, if there is a sprite at the
|
||||
* destination position, it must also be moved immediately following
|
||||
* this call (as in the case of a swap) to avoid badness.
|
||||
*
|
||||
* @return the piece sprite that is being moved.
|
||||
*/
|
||||
public Sprite movePiece (int sx, int sy, int tx, int ty, long duration)
|
||||
{
|
||||
int spos = sy * _bwid + sx;
|
||||
Sprite piece = _pieces[spos];
|
||||
if (piece == null) {
|
||||
Log.warning("Missing source sprite for drop [sx=" + sx +
|
||||
", sy=" + sy + ", tx=" + tx + ", ty=" + ty + "].");
|
||||
return null;
|
||||
}
|
||||
_pieces[spos] = null;
|
||||
movePiece(piece, sx, sy, tx, ty, duration);
|
||||
return piece;
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function for moving pieces into place.
|
||||
*/
|
||||
protected void movePiece (Sprite piece, final int sx, final int sy,
|
||||
final int tx, final int ty, long duration)
|
||||
{
|
||||
final Exception where = new Exception();
|
||||
|
||||
// if the sprite needn't move, then just position it and be done
|
||||
Point start = new Point();
|
||||
getPiecePosition(sx, sy, start);
|
||||
if (sx == tx && sy == ty) {
|
||||
int tpos = ty * _bwid + tx;
|
||||
if (_pieces[tpos] != null) {
|
||||
Log.warning("Zoiks! Asked to add a piece where we already " +
|
||||
"have one [sx=" + sx + ", sy=" + sy +
|
||||
", tx=" + tx + ", ty=" + ty + "].");
|
||||
Log.logStackTrace(where);
|
||||
return;
|
||||
}
|
||||
_pieces[tpos] = piece;
|
||||
piece.setLocation(start.x, start.y);
|
||||
return;
|
||||
}
|
||||
|
||||
// note that this piece is moving toward its destination
|
||||
final int tpos = ty * _bwid + tx;
|
||||
_moving[tpos] = true;
|
||||
|
||||
// then create a path and do some bits
|
||||
Point end = new Point();
|
||||
getPiecePosition(tx, ty, end);
|
||||
piece.addSpriteObserver(new PathAdapter() {
|
||||
public void pathCompleted (Sprite sprite, Path path, long when) {
|
||||
sprite.removeSpriteObserver(this);
|
||||
if (_pieces[tpos] != null) {
|
||||
Log.warning("Oh god, we're dropping onto another piece " +
|
||||
"[sx=" + sx + ", sy=" + sy +
|
||||
", tx=" + tx + ", ty=" + ty + "].");
|
||||
Log.logStackTrace(where);
|
||||
return;
|
||||
}
|
||||
_pieces[tpos] = sprite;
|
||||
if (_actionSprites.remove(sprite)) {
|
||||
maybeFireCleared();
|
||||
}
|
||||
pieceArrived(when, sprite, tx, ty);
|
||||
}
|
||||
});
|
||||
_actionSprites.add(piece);
|
||||
piece.move(new LinePath(start, end, duration));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a piece is finished moving into its requested position.
|
||||
* Derived classes may wish to take this opportunity to play a sound
|
||||
* or whatnot.
|
||||
*/
|
||||
protected void pieceArrived (long tickStamp, Sprite sprite, int px, int py)
|
||||
{
|
||||
_moving[py * _bwid + px] = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the piece that is supposed to be at the specified
|
||||
* coordinates is not yet there but is actually en route to that
|
||||
* location (due to a call to {@link #movePiece}).
|
||||
*/
|
||||
protected boolean isMoving (int px, int py)
|
||||
{
|
||||
return _moving[py * _bwid + px];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the image used to display the given piece at coordinates
|
||||
* <code>(0, 0)</code> with an orientation of {@link #NORTH}. This
|
||||
* serves as a convenience routine for those puzzles that don't bother
|
||||
* rendering their pieces differently when placed at different board
|
||||
* coordinates or in different orientations.
|
||||
*/
|
||||
public Mirage getPieceImage (int piece)
|
||||
{
|
||||
return getPieceImage(piece, 0, 0, NORTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the image used to display the given piece at the specified
|
||||
* column and row with the given orientation.
|
||||
*/
|
||||
public abstract Mirage getPieceImage (
|
||||
int piece, int col, int row, int orient);
|
||||
|
||||
// documentation inherited
|
||||
public void setBoard (Board board)
|
||||
{
|
||||
// when a new board arrives, we want to remove all drop sprites
|
||||
// so that they don't modify the new board with their old ideas
|
||||
for (Iterator iter = _actionSprites.iterator(); iter.hasNext(); ) {
|
||||
Sprite s = (Sprite) iter.next();
|
||||
if (s instanceof DropSprite) {
|
||||
// remove it from _sprites safely
|
||||
iter.remove();
|
||||
// but then use the standard removal method
|
||||
removeSprite(s);
|
||||
}
|
||||
}
|
||||
|
||||
// remove all of this board's piece sprites
|
||||
int pcount = (_pieces == null) ? 0 : _pieces.length;
|
||||
for (int ii = 0; ii < pcount; ii++) {
|
||||
if (_pieces[ii] != null) {
|
||||
removeSprite(_pieces[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
super.setBoard(board);
|
||||
|
||||
_dboard = (DropBoard)board;
|
||||
|
||||
// create the pieces for the new board
|
||||
Point spos = new Point();
|
||||
int width = _dboard.getWidth(), height = _dboard.getHeight();
|
||||
_pieces = new Sprite[width * height];
|
||||
for (int yy = 0; yy < height; yy++) {
|
||||
for (int xx = 0; xx < width; xx++) {
|
||||
Sprite piece = createPieceSprite(
|
||||
_dboard.getPiece(xx, yy), xx, yy);
|
||||
if (piece != null) {
|
||||
int ppos = yy * width + xx;
|
||||
getPiecePosition(xx, yy, spos);
|
||||
piece.setLocation(spos.x, spos.y);
|
||||
addSprite(piece);
|
||||
_pieces[ppos] = piece;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we use this to track when pieces are animating toward their new
|
||||
// position and are not yet in place
|
||||
_moving = new boolean[width * height];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the piece sprite at the specified location.
|
||||
*/
|
||||
public Sprite getPieceSprite (int xx, int yy)
|
||||
{
|
||||
return _pieces[yy * _dboard.getWidth() + xx];
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the specified piece from the board.
|
||||
*/
|
||||
public void clearPieceSprite (int xx, int yy)
|
||||
{
|
||||
int ppos = yy * _dboard.getWidth() + xx;
|
||||
if (_pieces[ppos] != null) {
|
||||
removeSprite(_pieces[ppos]);
|
||||
_pieces[ppos] = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears out a piece from the board along with its piece sprite.
|
||||
*/
|
||||
public void clearPiece (int xx, int yy)
|
||||
{
|
||||
_dboard.setPiece(xx, yy, PIECE_NONE);
|
||||
clearPieceSprite(xx, yy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new drop sprite used to animate the given pieces falling
|
||||
* in the specified column.
|
||||
*/
|
||||
public DropSprite createPieces (int col, int row, int[] pieces, int dist)
|
||||
{
|
||||
return new DropSprite(this, col, row, pieces, dist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dirties the rectangle encompassing the segment with the given
|
||||
* direction and length whose bottom-leftmost corner is at <code>(col,
|
||||
* row)</code>.
|
||||
*/
|
||||
public void dirtySegment (int dir, int col, int row, int len)
|
||||
{
|
||||
int x = _pwid * col, y = (_phei * row) - _roff;
|
||||
int wid = (dir == VERTICAL) ? _pwid : len * _pwid;
|
||||
int hei = (dir == VERTICAL) ? _phei * len : _phei;
|
||||
_remgr.invalidateRegion(x, y, wid, hei);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns an animation showing the specified score
|
||||
* floating up the view, with the label initially centered within the
|
||||
* view.
|
||||
*
|
||||
* @param score the score text to display.
|
||||
* @param color the color of the text.
|
||||
* @param font the font.
|
||||
*/
|
||||
public ScoreAnimation createScoreAnimation (
|
||||
String score, Color color, Font font)
|
||||
{
|
||||
return createScoreAnimation(
|
||||
score, color, font, 0, _bhei - 1, _bwid, _bhei);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns an animation showing the specified score
|
||||
* floating up the view.
|
||||
*
|
||||
* @param score the score text to display.
|
||||
* @param color the color of the text.
|
||||
* @param font the font to use.
|
||||
* @param x the left coordinate in board coordinates of the rectangle
|
||||
* within which the score is to be centered.
|
||||
* @param y the bottom coordinate in board coordinates of the
|
||||
* rectangle within which the score is to be centered.
|
||||
* @param width the width in board coordinates of the rectangle within
|
||||
* which the score is to be centered.
|
||||
* @param height the height in board coordinates of the rectangle
|
||||
* within which the score is to be centered.
|
||||
*/
|
||||
public ScoreAnimation createScoreAnimation (String score, Color color,
|
||||
Font font, int x, int y,
|
||||
int width, int height)
|
||||
{
|
||||
// create the score animation
|
||||
ScoreAnimation anim =
|
||||
createScoreAnimation(score, color, font, x, y);
|
||||
|
||||
// position the label within the specified rectangle
|
||||
Dimension lsize = anim.getLabel().getSize();
|
||||
Point pos = new Point();
|
||||
centerRectInBoardRect(
|
||||
x, y, width, height, lsize.width, lsize.height, pos);
|
||||
anim.setLocation(pos.x, pos.y);
|
||||
|
||||
return anim;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the sprite that is used to display the specified piece. If
|
||||
* the piece represents no piece, this method should return null.
|
||||
*/
|
||||
protected Sprite createPieceSprite (int piece, int px, int py)
|
||||
{
|
||||
if (piece == PIECE_NONE) {
|
||||
return null;
|
||||
}
|
||||
ImageSprite sprite = new ImageSprite(
|
||||
getPieceImage(piece, px, py, NORTH));
|
||||
sprite.setRenderOrder(-1);
|
||||
return sprite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates <code>pos</code> with the most appropriate screen
|
||||
* coordinates to center a rectangle of the given width and height (in
|
||||
* pixels) within the specified rectangle (in board coordinates).
|
||||
*
|
||||
* @param bx the bounding rectangle's left board coordinate.
|
||||
* @param by the bounding rectangle's bottom board coordinate.
|
||||
* @param bwid the bounding rectangle's width in board coordinates.
|
||||
* @param bhei the bounding rectangle's height in board coordinates.
|
||||
* @param rwid the width of the rectangle to position in pixels.
|
||||
* @param rhei the height of the rectangle to position in pixels.
|
||||
* @param pos the point to populate with the rectangle's final
|
||||
* position.
|
||||
*/
|
||||
protected void centerRectInBoardRect (
|
||||
int bx, int by, int bwid, int bhei, int rwid, int rhei, Point pos)
|
||||
{
|
||||
getPiecePosition(bx, by + 1, pos);
|
||||
pos.x += (((bwid * _pwid) - rwid) / 2);
|
||||
pos.y -= ((((bhei * _phei) - rhei) / 2) + rhei);
|
||||
|
||||
// constrain to fit wholly within the board bounds
|
||||
pos.x = Math.max(Math.min(pos.x, _bounds.width - rwid), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates the given drop block sprite to the specified orientation,
|
||||
* updating the image as necessary. Derived classes that make use of
|
||||
* block dropping functionality should override this method to do the
|
||||
* right thing.
|
||||
*/
|
||||
public void rotateDropBlock (DropBlockSprite sprite, int orient)
|
||||
{
|
||||
// nothing for now
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void paintBetween (Graphics2D gfx, Rectangle dirtyRect)
|
||||
{
|
||||
gfx.translate(0, -_roff);
|
||||
renderBoard(gfx, dirtyRect);
|
||||
renderRisingPieces(gfx, dirtyRect);
|
||||
gfx.translate(0, _roff);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public Dimension getPreferredSize ()
|
||||
{
|
||||
int wid = _bwid * _pwid;
|
||||
int hei = _bhei * _phei;
|
||||
return new Dimension(wid, hei);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the row of rising pieces to the given graphics context.
|
||||
* Sub-classes that make use of board rising functionality should
|
||||
* override this method to draw the rising piece row.
|
||||
*/
|
||||
protected void renderRisingPieces (Graphics2D gfx, Rectangle dirtyRect)
|
||||
{
|
||||
// nothing for now
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the board rising offset to the given y-position.
|
||||
*/
|
||||
protected void setRiseOffset (int y)
|
||||
{
|
||||
if (y != _roff) {
|
||||
_roff = y;
|
||||
_remgr.invalidateRegion(_bounds);
|
||||
}
|
||||
}
|
||||
|
||||
/** The drop board. */
|
||||
protected DropBoard _dboard;
|
||||
|
||||
/** A sprite for every piece displayed in the drop board. */
|
||||
protected Sprite[] _pieces;
|
||||
|
||||
/** Indicates whether a piece is en route to its destination. */
|
||||
protected boolean[] _moving;
|
||||
|
||||
/** The piece dimensions in pixels. */
|
||||
protected int _pwid, _phei;
|
||||
|
||||
/** The board rising offset. */
|
||||
protected int _roff;
|
||||
|
||||
/** The board dimensions in pieces. */
|
||||
protected int _bwid, _bhei;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// $Id: DropPanel.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.drop.client;
|
||||
|
||||
import com.threerings.puzzle.data.BoardSummary;
|
||||
|
||||
/**
|
||||
* Puzzles using the drop services need implement this interface to
|
||||
* display drop puzzle related information.
|
||||
*/
|
||||
public interface DropPanel
|
||||
{
|
||||
/**
|
||||
* Sets the next block to be displayed.
|
||||
*/
|
||||
public void setNextBlock (int[] pieces);
|
||||
|
||||
/**
|
||||
* Updates the board summary display for the given player.
|
||||
*/
|
||||
public void setSummary (int pidx, BoardSummary summary);
|
||||
}
|
||||
@@ -0,0 +1,584 @@
|
||||
//
|
||||
// $Id: DropSprite.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.drop.client;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Point;
|
||||
import java.awt.Shape;
|
||||
|
||||
import com.samskivert.util.ObserverList;
|
||||
import com.threerings.util.DirectionUtil;
|
||||
|
||||
import com.threerings.media.image.Mirage;
|
||||
import com.threerings.media.sprite.Sprite;
|
||||
|
||||
import com.threerings.puzzle.Log;
|
||||
|
||||
/**
|
||||
* The drop sprite is a sprite that displays one or more pieces falling
|
||||
* toward the bottom of the board.
|
||||
*/
|
||||
public class DropSprite extends Sprite
|
||||
{
|
||||
/**
|
||||
* Constructs a drop sprite and starts it dropping.
|
||||
*
|
||||
* @param view the board view upon which this sprite will be displayed.
|
||||
* @param col the column of the sprite.
|
||||
* @param row the row of the bottom-most piece.
|
||||
* @param pieces the pieces displayed by the sprite.
|
||||
* @param dist the distance the sprite is to drop in rows.
|
||||
*/
|
||||
public DropSprite (
|
||||
DropBoardView view, int col, int row, int[] pieces, int dist)
|
||||
{
|
||||
this(view, col, row, pieces, dist, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a drop sprite and starts it dropping.
|
||||
*
|
||||
* @param view the board view upon which this sprite will be displayed.
|
||||
* @param col the column of the sprite.
|
||||
* @param row the row of the bottom-most piece.
|
||||
* @param pieces the pieces displayed by the sprite.
|
||||
* @param dist the distance the sprite is to drop in rows.
|
||||
* @param renderOrder the render order.
|
||||
*/
|
||||
public DropSprite (
|
||||
DropBoardView view, int col, int row, int[] pieces, int dist,
|
||||
int renderOrder)
|
||||
{
|
||||
_view = view;
|
||||
_col = col;
|
||||
_row = row;
|
||||
_pieces = pieces;
|
||||
_dist = (dist == 0) ? 1 : dist;
|
||||
_orient = NORTH;
|
||||
_unit = _view.getPieceHeight();
|
||||
setRenderOrder(renderOrder);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void init ()
|
||||
{
|
||||
super.init();
|
||||
|
||||
// size the bounds to fit our pieces
|
||||
updateBounds();
|
||||
// set up the piece location
|
||||
setBoardLocation(_row, _col);
|
||||
// calculate vertical render offset based on the number of pieces
|
||||
setRowOffset(-(_pieces.length - 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the remaining number of columns to drop.
|
||||
*/
|
||||
public int getDistance ()
|
||||
{
|
||||
return _dist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the column the piece is located in.
|
||||
*/
|
||||
public int getColumn ()
|
||||
{
|
||||
return _col;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the row the piece is located in.
|
||||
*/
|
||||
public int getRow ()
|
||||
{
|
||||
return _row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pieces the sprite is displaying.
|
||||
*/
|
||||
public int[] getPieces ()
|
||||
{
|
||||
return _pieces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the velocity of this sprite.
|
||||
*/
|
||||
public float getVelocity ()
|
||||
{
|
||||
return _vel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the row and column the piece is located in.
|
||||
*/
|
||||
public void setBoardLocation (int row, int col)
|
||||
{
|
||||
_row = row;
|
||||
_col = col;
|
||||
updatePosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the column the piece is located in.
|
||||
*/
|
||||
public void setColumn (int col)
|
||||
{
|
||||
_col = col;
|
||||
updatePosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the row the piece is located in.
|
||||
*/
|
||||
public void setRow (int row)
|
||||
{
|
||||
_row = row;
|
||||
updatePosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the column offset of the sprite image.
|
||||
*/
|
||||
public void setColumnOffset (int count)
|
||||
{
|
||||
_offx = count;
|
||||
updateRenderOffset();
|
||||
updateRenderOrigin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the row offset of the sprite image.
|
||||
*/
|
||||
public void setRowOffset (int count)
|
||||
{
|
||||
_offy = count;
|
||||
updateRenderOffset();
|
||||
updateRenderOrigin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pieces the sprite is displaying.
|
||||
*/
|
||||
public void setPieces (int[] pieces)
|
||||
{
|
||||
_pieces = pieces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the velocity of this sprite. The time at which the current
|
||||
* row was entered is modified so that the sprite position will remain
|
||||
* the same when calculated using the new velocity since the piece
|
||||
* sprite may have its velocity modified in the middle of a row
|
||||
* traversal.
|
||||
*/
|
||||
public void setVelocity (float velocity)
|
||||
{
|
||||
// bail if we've already got the requested velocity
|
||||
if (_vel == velocity) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_rowstamp > 0) {
|
||||
// get our current distance along the row
|
||||
long now = _view.getTimeStamp();
|
||||
float pctdone = getPercentDone(now);
|
||||
|
||||
// revise the current row entry time to account for the new velocity
|
||||
float travpix = pctdone * _unit;
|
||||
long msecs = (long)(travpix / velocity);
|
||||
_rowstamp = now - msecs;
|
||||
}
|
||||
|
||||
// update the velocity
|
||||
_vel = velocity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the piece dropping toward the next row.
|
||||
*/
|
||||
public void drop ()
|
||||
{
|
||||
// Log.info("Dropping piece [piece=" + this + "].");
|
||||
|
||||
// drop one row by default
|
||||
if (_dist <= 0) {
|
||||
_dist = 1;
|
||||
}
|
||||
|
||||
if (_stopstamp > 0) {
|
||||
// we're dropping from a stand-still
|
||||
long delta = _view.getTimeStamp() - _stopstamp;
|
||||
_rowstamp += delta;
|
||||
_stopstamp = 0;
|
||||
|
||||
} else {
|
||||
// we're continuing a previous drop, so make use of any
|
||||
// previously existing time
|
||||
_rowstamp = _endstamp;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this drop sprite is dropping, false if it has been
|
||||
* {@link #stop}ped or has not yet been {@link #drop}ped.
|
||||
*/
|
||||
public boolean isDropping ()
|
||||
{
|
||||
return (_stopstamp == 0) && (_rowstamp != 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the piece from dropping.
|
||||
*/
|
||||
public void stop ()
|
||||
{
|
||||
if (_stopstamp == 0) {
|
||||
_stopstamp = _view.getTimeStamp();
|
||||
// Log.info("Stopped piece [piece=" + this + "].");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the drop sprite into (or takes it out of) bouncing
|
||||
* mode. Bouncing mode is used to put the sprite into limbo after it
|
||||
* lands but before we commit the landing, giving the user a last
|
||||
* moment change move or rotate the piece. While the sprite is
|
||||
* "bouncing" it will be rendered one pixel below it's at rest state.
|
||||
*/
|
||||
public void setBouncing (boolean bouncing)
|
||||
{
|
||||
if (_bouncing = bouncing) {
|
||||
// if we've activated bouncing, shift the sprite slightly to
|
||||
// illustrate its new state
|
||||
shiftForBounce();
|
||||
|
||||
// to prevent funny business in the event that we were a long
|
||||
// ways past the end of the row when we landed, we warp the
|
||||
// sprite back to the exact point of landing for the purposes
|
||||
// of the bounce and any subsequent antics
|
||||
_endstamp = _rowstamp = _view.getTimeStamp();
|
||||
|
||||
// Log.info("Adjusted rowstap due to bounce " +
|
||||
// "[time=" + _endstamp + "].");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this sprite is bouncing.
|
||||
*/
|
||||
public boolean isBouncing ()
|
||||
{
|
||||
return _bouncing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the sprite's location to illustrate that it is currently in
|
||||
* the "bouncing" state.
|
||||
*/
|
||||
protected void shiftForBounce ()
|
||||
{
|
||||
setLocation(_ox, _srcPos.y+1);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public boolean inside (Shape shape)
|
||||
{
|
||||
return shape.contains(_bounds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a value between <code>0.0</code> and <code>1.0</code>
|
||||
* representing how far the piece has moved toward the next row
|
||||
* as of the given time stamp.
|
||||
*/
|
||||
public float getPercentDone (long timestamp)
|
||||
{
|
||||
// if we've never been ticked and so haven't yet initialized our
|
||||
// row start timestamp, just let the caller know that we've not
|
||||
// traversed our row at all
|
||||
if (_rowstamp == 0) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
long msecs = Math.max(0, timestamp - _rowstamp);
|
||||
float travpix = msecs * _vel;
|
||||
float pctdone = (travpix / _unit);
|
||||
|
||||
// Log.info("getPercentDone [timestamp=" + timestamp +
|
||||
// ", rowstamp=" + _rowstamp + ", msecs=" + msecs +
|
||||
// ", travpix=" + travpix + ", pctdone=" + pctdone +
|
||||
// ", vel=" + _vel + "].");
|
||||
|
||||
return pctdone;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void paint (Graphics2D gfx)
|
||||
{
|
||||
// get the column and row increment based on the sprite's orientation
|
||||
int oidx = _orient/2;
|
||||
int incx = ORIENT_DX[oidx];
|
||||
int incy = ORIENT_DY[oidx];
|
||||
|
||||
// determine offset from the start of each actual row and column
|
||||
int dx = _ox - _srcPos.x, dy = _oy - _srcPos.y;
|
||||
|
||||
int pcol = _col, prow = _row;
|
||||
for (int ii = 0; ii < _pieces.length; ii++) {
|
||||
// ask the board for the render position of this piece
|
||||
_view.getPiecePosition(pcol, prow, _renderPos);
|
||||
// draw the piece image
|
||||
paintPieceImage(gfx, ii, pcol, prow, _orient,
|
||||
_renderPos.x + dx, _renderPos.y + dy);
|
||||
// increment the target column and row
|
||||
pcol += incx;
|
||||
prow += incy;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Paints the specified piece with the supplied parameters.
|
||||
*/
|
||||
protected void paintPieceImage (Graphics2D gfx, int pieceidx,
|
||||
int col, int row, int orient, int x, int y)
|
||||
{
|
||||
Mirage image = _view.getPieceImage(_pieces[pieceidx], col, row, orient);
|
||||
image.paint(gfx, x, y);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void tick (long timestamp)
|
||||
{
|
||||
super.tick(timestamp);
|
||||
|
||||
// initialize our rowstamp if we haven't done so already
|
||||
if (_rowstamp == 0) {
|
||||
_rowstamp = timestamp;
|
||||
}
|
||||
|
||||
// if we're bouncing or paused, do nothing here
|
||||
if (_bouncing || _stopstamp > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
PieceMovedOp pmop = null;
|
||||
|
||||
// figure out how far along the current board coordinate we should be
|
||||
float pctdone = getPercentDone(timestamp);
|
||||
if (pctdone >= 1.0f) {
|
||||
// note that we've reached the next row
|
||||
advancePosition();
|
||||
|
||||
// update remaining drop distance
|
||||
_dist--;
|
||||
|
||||
// calculate any remaining time to be used
|
||||
long used = (long)(_unit / _vel);
|
||||
_endstamp = _rowstamp + used;
|
||||
_rowstamp = _endstamp;
|
||||
|
||||
// update our percent done because we've moved down a row
|
||||
pctdone -= 1.0;
|
||||
|
||||
// inform observers that we've reached our destination
|
||||
pmop = new PieceMovedOp(this, timestamp, _col, _row);
|
||||
}
|
||||
|
||||
// constrain the sprite's position to the destination row
|
||||
pctdone = Math.min(pctdone, 1.0f);
|
||||
|
||||
// calculate the latest sprite position
|
||||
int nx = _srcPos.x + (int)((_destPos.x - _srcPos.x) * pctdone);
|
||||
int ny = _srcPos.y + (int)((_destPos.y - _srcPos.y) * pctdone);
|
||||
|
||||
// Log.info("Drop sprite tick [dist=" + _dist + ", pctdone=" + pctdone +
|
||||
// ", row=" + _row + ", col=" + _col +
|
||||
// ", nx=" + nx + ", ny=" + ny + "].");
|
||||
|
||||
// only update the sprite's location if it actually moved
|
||||
if (_ox != nx || _oy != ny) {
|
||||
setLocation(nx, ny);
|
||||
}
|
||||
|
||||
// lastly notify our observers if we made it to the next row
|
||||
if (pmop != null) {
|
||||
_observers.apply(pmop);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the sprite has finished traversing its current row to
|
||||
* advance its board coordinates to the next row.
|
||||
*/
|
||||
protected void advancePosition ()
|
||||
{
|
||||
setRow(_row + 1);
|
||||
// Log.info("Moved to row " + _row);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void fastForward (long timeDelta)
|
||||
{
|
||||
if (_rowstamp > 0) {
|
||||
_rowstamp += timeDelta;
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void toString (StringBuilder buf)
|
||||
{
|
||||
super.toString(buf);
|
||||
buf.append(", orient=").append(DirectionUtil.toShortString(_orient));
|
||||
buf.append(", row=").append(_row);
|
||||
buf.append(", col=").append(_col);
|
||||
buf.append(", offx=").append(_offx);
|
||||
buf.append(", offy=").append(_offy);
|
||||
buf.append(", dist=").append(_dist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates internal pixel coordinates used when the piece is moving.
|
||||
*/
|
||||
protected void updatePosition ()
|
||||
{
|
||||
_view.getPiecePosition(_col, _row, _srcPos);
|
||||
_view.getPiecePosition(_col, _row+1, _destPos);
|
||||
setLocation(_srcPos.x, _srcPos.y);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void setOrientation (int orient)
|
||||
{
|
||||
invalidate();
|
||||
super.setOrientation(orient);
|
||||
updateBounds();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the bounds for this sprite based on the sprite display
|
||||
* dimensions in the view.
|
||||
*/
|
||||
protected void updateBounds ()
|
||||
{
|
||||
Dimension size = _view.getPieceSegmentSize(
|
||||
_col, _row, _orient, _pieces.length);
|
||||
_bounds.width = size.width;
|
||||
_bounds.height = size.height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts our render origin such that our location is not in the
|
||||
* upper left of the sprite's rendered image but is in fact offset by
|
||||
* some number of rows and columns.
|
||||
*/
|
||||
protected void updateRenderOffset ()
|
||||
{
|
||||
_oxoff = -(_view.getPieceWidth() * _offx);
|
||||
_oyoff = -(_view.getPieceHeight() * _offy);
|
||||
}
|
||||
|
||||
/** Used to dispatch {@link DropSpriteObserver#pieceMoved}. */
|
||||
protected static class PieceMovedOp implements ObserverList.ObserverOp
|
||||
{
|
||||
public PieceMovedOp (DropSprite sprite, long when, int col, int row)
|
||||
{
|
||||
_sprite = sprite;
|
||||
_when = when;
|
||||
_col = col;
|
||||
_row = row;
|
||||
}
|
||||
|
||||
public boolean apply (Object observer)
|
||||
{
|
||||
if (observer instanceof DropSpriteObserver) {
|
||||
((DropSpriteObserver)observer).pieceMoved(
|
||||
_sprite, _when, _col, _row);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected DropSprite _sprite;
|
||||
protected long _when;
|
||||
protected int _col, _row;
|
||||
}
|
||||
|
||||
/** The default piece velocity. */
|
||||
protected static final float DEFAULT_VELOCITY = 30f/1000f;
|
||||
|
||||
/** The time at which we started the current row. */
|
||||
protected long _rowstamp;
|
||||
|
||||
/** The time at which we reached the end of the previous row. */
|
||||
protected long _endstamp;
|
||||
|
||||
/** The time at which we were stopped en route to our next row. */
|
||||
protected long _stopstamp;
|
||||
|
||||
/** The board view upon which this sprite is displayed. */
|
||||
protected DropBoardView _view;
|
||||
|
||||
/** The unit distance the sprite moves to reach the next row. */
|
||||
protected int _unit;
|
||||
|
||||
/** The screen coordinates of the top-left of the row currently
|
||||
* occupied by the sprite. */
|
||||
protected Point _srcPos = new Point();
|
||||
|
||||
/** The screen coordinates of the top-left of the row toward which the
|
||||
* sprite is falling. */
|
||||
protected Point _destPos = new Point();
|
||||
|
||||
/** The piece render position; used as working data when determining
|
||||
* where to render each piece in the sprite. */
|
||||
protected Point _renderPos = new Point();
|
||||
|
||||
/** The number of rows remaining to drop. */
|
||||
protected int _dist;
|
||||
|
||||
/** The piece velocity. */
|
||||
protected float _vel = DEFAULT_VELOCITY;
|
||||
|
||||
/** The offsets in columns or rows at which the piece is rendered. */
|
||||
protected int _offx, _offy;
|
||||
|
||||
/** The current piece location in the board. */
|
||||
protected int _row, _col;
|
||||
|
||||
/** The pieces this sprite is displaying. */
|
||||
protected int[] _pieces;
|
||||
|
||||
/** Indicates that the drop sprite is bouncing; see {@link
|
||||
* #setBouncing}. */
|
||||
protected boolean _bouncing;
|
||||
|
||||
// used to compute the column and row increment while rendering the
|
||||
// sprite's pieces based on its orientation
|
||||
// W N E S
|
||||
protected static final int[] ORIENT_DX = { -1, 0, 1, 0 };
|
||||
protected static final int[] ORIENT_DY = { 0, -1, 0, 1 };
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// $Id: DropSpriteObserver.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.drop.client;
|
||||
|
||||
/**
|
||||
* Provides notifications for drop puzzle specific stuff.
|
||||
*/
|
||||
public interface DropSpriteObserver
|
||||
{
|
||||
/**
|
||||
* Called when the drop sprite has moved completely to the specified
|
||||
* board coordinates.
|
||||
*/
|
||||
public void pieceMoved (DropSprite sprite, long when, int col, int row);
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
//
|
||||
// $Id: NextBlockView.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.drop.client;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import com.threerings.media.image.Mirage;
|
||||
import com.threerings.util.DirectionCodes;
|
||||
|
||||
/**
|
||||
* The next block view displays an image representing the next drop block
|
||||
* to appear in the game.
|
||||
*/
|
||||
public class NextBlockView extends JComponent
|
||||
implements DirectionCodes
|
||||
{
|
||||
/**
|
||||
* Constructs a next block view.
|
||||
*/
|
||||
public NextBlockView (DropBoardView view, int pwid, int phei, int orient)
|
||||
{
|
||||
// save things off
|
||||
_view = view;
|
||||
_pwid = pwid;
|
||||
_phei = phei;
|
||||
_orient = orient;
|
||||
|
||||
// configure the component
|
||||
setOpaque(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pieces displayed by the view.
|
||||
*/
|
||||
public void setPieces (int[] pieces)
|
||||
{
|
||||
_pieces = pieces;
|
||||
repaint();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void paintComponent (Graphics g)
|
||||
{
|
||||
super.paintComponent(g);
|
||||
|
||||
// draw the pieces
|
||||
Graphics2D gfx = (Graphics2D)g;
|
||||
if (_pieces != null) {
|
||||
Dimension size = getSize();
|
||||
int xpos = (_orient == VERTICAL) ? 0 : (size.width - _pwid);
|
||||
int ypos = (_orient == VERTICAL) ? (size.height - _phei) : 0;
|
||||
|
||||
for (int ii = 0; ii < _pieces.length; ii++) {
|
||||
Mirage image = _view.getPieceImage(_pieces[ii]);
|
||||
image.paint(gfx, xpos, ypos);
|
||||
if (_orient == VERTICAL) {
|
||||
ypos -= _phei;
|
||||
} else {
|
||||
xpos -= _pwid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public Dimension getPreferredSize ()
|
||||
{
|
||||
int wid = (_orient == VERTICAL) ? _pwid : (2 * _pwid);
|
||||
int hei = (_orient == VERTICAL) ? (2 * _phei) : _phei;
|
||||
return new Dimension(wid, hei);
|
||||
}
|
||||
|
||||
/** The drop board view from which we obtain piece images. */
|
||||
protected DropBoardView _view;
|
||||
|
||||
/** The pieces displayed by this view. */
|
||||
protected int[] _pieces;
|
||||
|
||||
/** The piece dimensions in pixels. */
|
||||
protected int _pwid, _phei;
|
||||
|
||||
/** The view orientation; one of {@link #HORIZONTAL} or {@link
|
||||
* #VERTICAL}. */
|
||||
protected int _orient;
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
//
|
||||
// $Id: PieceGroupAnimation.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.drop.client;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
|
||||
import com.threerings.media.animation.Animation;
|
||||
import com.threerings.media.sprite.ImageSprite;
|
||||
import com.threerings.media.sprite.PathObserver;
|
||||
import com.threerings.media.sprite.Sprite;
|
||||
import com.threerings.media.util.Path;
|
||||
|
||||
import com.threerings.puzzle.drop.data.DropBoard;
|
||||
import com.threerings.puzzle.drop.data.DropPieceCodes;
|
||||
|
||||
/**
|
||||
* Animates all the pieces on a puzzle board doing some sort of global
|
||||
* effect like all flying into place or out into the ether.
|
||||
*/
|
||||
public abstract class PieceGroupAnimation extends Animation
|
||||
implements PathObserver
|
||||
{
|
||||
/**
|
||||
* Creates a piece group animation which must be initialized with a
|
||||
* subsequent call to {@link #init}.
|
||||
*/
|
||||
public PieceGroupAnimation (DropBoardView view, DropBoard board)
|
||||
{
|
||||
super(new Rectangle(0, 0, 0, 0)); // we don't render ourselves
|
||||
_view = view;
|
||||
_board = board;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void tick (long tickStamp)
|
||||
{
|
||||
// nothing doing
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void paint (Graphics2D gfx)
|
||||
{
|
||||
// nothing doing
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void pathCancelled (Sprite sprite, Path path)
|
||||
{
|
||||
_finished = (--_penders == 0);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void pathCompleted (Sprite sprite, Path path, long when)
|
||||
{
|
||||
_finished = (--_penders == 0);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void willStart (long tickStamp)
|
||||
{
|
||||
super.willStart(tickStamp);
|
||||
|
||||
// create an image sprite for every piece on the board and set
|
||||
// them on their paths
|
||||
int width = _board.getWidth(), height = _board.getHeight();
|
||||
_sprites = new Sprite[width * height];
|
||||
for (int yy = 0; yy < height; yy++) {
|
||||
for (int xx = 0; xx < width; xx++) {
|
||||
int spos = yy*width+xx;
|
||||
_sprites[spos] = _view.getPieceSprite(xx, yy);
|
||||
if (_sprites[spos] != null) {
|
||||
configureSprite(_sprites[spos], xx, yy);
|
||||
_sprites[spos].addSpriteObserver(this);
|
||||
_penders++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An animation must override this method to configure each sprite
|
||||
* with a path, potentially a render order, and whatever other
|
||||
* configurations are needed.
|
||||
*/
|
||||
protected abstract void configureSprite (Sprite sprite, int xx, int yy);
|
||||
|
||||
protected DropBoardView _view;
|
||||
protected DropBoard _board;
|
||||
protected Sprite[] _sprites;
|
||||
protected int _penders;
|
||||
}
|
||||
@@ -0,0 +1,887 @@
|
||||
//
|
||||
// $Id: DropBoard.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.drop.data;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import com.threerings.util.DirectionUtil;
|
||||
|
||||
import com.threerings.puzzle.Log;
|
||||
import com.threerings.puzzle.data.Board;
|
||||
import com.threerings.puzzle.drop.client.DropControllerDelegate;
|
||||
import com.threerings.puzzle.drop.util.DropBoardUtil;
|
||||
|
||||
/**
|
||||
* A class that provides for various useful logical operations to be
|
||||
* enacted on a two-dimensional board and provides an easier mechanism for
|
||||
* referencing pieces by position.
|
||||
*/
|
||||
public class DropBoard extends Board
|
||||
implements DropPieceCodes
|
||||
{
|
||||
/** The rotation constant for rotation around a central piece. */
|
||||
public static final int RADIAL_ROTATION = 0;
|
||||
|
||||
/** The rotation constant for rotation wherein the block occupies the
|
||||
* same columns when rotating. */
|
||||
public static final int INPLACE_ROTATION = 1;
|
||||
|
||||
/** An operation that does naught but clear pieces, which proves to be
|
||||
* generally useful. */
|
||||
public static final PieceOperation CLEAR_OP = new PieceOperation () {
|
||||
public boolean execute (DropBoard board, int col, int row) {
|
||||
board.setPiece(col, row, PIECE_NONE);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* An interface to be implemented by classes that would like to apply
|
||||
* some operation to each piece in a column or row segment in the
|
||||
* board.
|
||||
*/
|
||||
public interface PieceOperation
|
||||
{
|
||||
/**
|
||||
* Called for each piece in the board segment the operation is
|
||||
* being applied to.
|
||||
*
|
||||
* @return true if the operation should continue to be applied if
|
||||
* being applied to multiple pieces, or false if it should
|
||||
* terminate after this application.
|
||||
*/
|
||||
public boolean execute (DropBoard board, int col, int row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an empty drop board for use when unserializing.
|
||||
*/
|
||||
public DropBoard ()
|
||||
{
|
||||
this(null, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a drop board of the given dimensions with its
|
||||
* pieces initialized to PIECE_NONE.
|
||||
*/
|
||||
public DropBoard (int bwid, int bhei)
|
||||
{
|
||||
this(new int[bwid*bhei], bwid, bhei);
|
||||
fill(PIECE_NONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a drop board of the given dimensions with its
|
||||
* pieces initialized to the given piece.
|
||||
*/
|
||||
public DropBoard (int bwid, int bhei, int piece)
|
||||
{
|
||||
this(new int[bwid*bhei], bwid, bhei);
|
||||
fill(piece);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a drop board with the given board and dimensions.
|
||||
*/
|
||||
public DropBoard (int[] board, int bwid, int bhei)
|
||||
{
|
||||
_board = board;
|
||||
_bwid = bwid;
|
||||
_bhei = bhei;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width of the board in columns.
|
||||
*/
|
||||
public int getWidth()
|
||||
{
|
||||
return _bwid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the height of the board in rows.
|
||||
*/
|
||||
public int getHeight()
|
||||
{
|
||||
return _bhei;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the piece at the given column and row in the board.
|
||||
*/
|
||||
public int getPiece (int col, int row)
|
||||
{
|
||||
try {
|
||||
return _board[(row*_bwid) + col];
|
||||
} catch (Exception e) {
|
||||
Log.warning("Failed getting piece [col=" + col +
|
||||
", row=" + row + ", error=" + e + "].");
|
||||
Log.logStackTrace(e);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For boards that are always filled, this method is called to obtain
|
||||
* pieces to fill the board.
|
||||
*/
|
||||
public int getNextPiece ()
|
||||
{
|
||||
return PIECE_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the distance the piece at the given column and row can drop
|
||||
* until it hits a non-empty piece (defined as {@link #PIECE_NONE}).
|
||||
*/
|
||||
public int getDropDistance (int col, int row)
|
||||
{
|
||||
int dist = 0;
|
||||
for (int yy = row + 1; yy < _bhei; yy++) {
|
||||
if (getPiece(col, yy) != PIECE_NONE) {
|
||||
return dist;
|
||||
}
|
||||
dist++;
|
||||
}
|
||||
return dist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given row in the board is empty.
|
||||
*/
|
||||
public boolean isRowEmpty (int row)
|
||||
{
|
||||
for (int col = 0; col < _bwid; col++) {
|
||||
if (getPiece(col, row) != PIECE_NONE) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether all of the pieces at the given coordinates can be
|
||||
* dropped one row.
|
||||
*/
|
||||
public boolean isValidDrop (int[] rows, int[] cols, float pctdone)
|
||||
{
|
||||
int bottom = _bhei - 1;
|
||||
for (int ii = 0; ii < rows.length; ii++) {
|
||||
// pieces at bottom can't be dropped
|
||||
if (rows[ii] >= bottom) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// pieces with pieces below them can't be dropped
|
||||
int row = rows[ii] + 1;
|
||||
if (row >= 0 && getPiece(cols[ii], row) != PIECE_NONE) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the specified coordinate is within the bounds of
|
||||
* the board, false if it is not.
|
||||
*/
|
||||
public boolean inBounds (int col, int row)
|
||||
{
|
||||
return (col >= 0 && row >= 0 && col < getWidth() && row < getHeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the specified block in the board is empty. The
|
||||
* block is allowed to occupy space off the top of the board as long
|
||||
* as it is within the horizontal board bounds.
|
||||
*
|
||||
* @param col the left coordinate of the block.
|
||||
* @param row the bottom coordinate of the block.
|
||||
* @param wid the width of the block.
|
||||
* @param hei the height of the block.
|
||||
*/
|
||||
public boolean isBlockEmpty (int col, int row, int wid, int hei)
|
||||
{
|
||||
for (int ypos = row; ypos > (row - hei); ypos--) {
|
||||
for (int xpos = col; xpos < (col + wid); xpos++) {
|
||||
// only allow movement off the top of the board that's
|
||||
// within the horizontal screen bounds and in a column
|
||||
// that's not topped out
|
||||
if (ypos < 0) {
|
||||
if ((xpos < 0 || xpos >= _bwid) ||
|
||||
(getPiece(xpos, 0) != PIECE_NONE)) {
|
||||
return false;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// don't allow movement outside the side or bottom bounds
|
||||
if (xpos < 0 ||
|
||||
xpos >= _bwid ||
|
||||
ypos >= _bhei) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// make sure no piece is present
|
||||
if (getPiece(xpos, ypos) != PIECE_NONE) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates the given block in the given direction and returns its
|
||||
* final state as <code>(orient, col, row, popped)</code>, where
|
||||
* <code>orient</code> is the final orientation of the drop block;
|
||||
* <code>col</code> and <code>row</code> are the final column and row
|
||||
* coordinates, respectively, of the central drop block piece.
|
||||
* <code>popped</code> will be set to 1 if the piece was popped up, 0
|
||||
* otherwise.
|
||||
*/
|
||||
public int[] getForgivingRotation (
|
||||
int[] rows, int[] cols, int orient, int dir, int rtype, float pctdone,
|
||||
boolean canPopup)
|
||||
{
|
||||
int px = cols[0], py = rows[0];
|
||||
|
||||
// Log.info("Starting rotation [px=" + px + ", py=" + py +
|
||||
// ", orient=" + orient + ", pctdone=" + pctdone + "].");
|
||||
|
||||
// try rotating the block in the given direction through all four
|
||||
// possible orientations
|
||||
for (int ii = 0; ii < 4; ii++) {
|
||||
int oidx = orient/2;
|
||||
|
||||
// adjust the position of the central piece
|
||||
px += ROTATE_DX[rtype][dir][oidx];
|
||||
py += ROTATE_DY[rtype][dir][oidx];
|
||||
|
||||
// update the orientation
|
||||
orient = DropBoardUtil.getRotatedOrientation(orient, dir);
|
||||
oidx = orient/2;
|
||||
|
||||
// because isBlockEmpty() always assumes the origin of the
|
||||
// block is in the lower-left, we need to adjust the
|
||||
// coordinates of the drop block's "central" piece accordingly
|
||||
int ox = px + ORIENT_ORIGIN_DX[oidx];
|
||||
int oy = py + ORIENT_ORIGIN_DY[oidx];
|
||||
|
||||
// if we're less than 50 percent through with our fall, we
|
||||
// want to check our current coordinates for validity; if
|
||||
// we're more, we want to check the row below our current
|
||||
// coordinates
|
||||
if (pctdone > 0.5) {
|
||||
oy += 1;
|
||||
}
|
||||
|
||||
// try each of three coercions: nothing, one left, one right
|
||||
for (int c = 0; c < COERCE_DX.length; c++) {
|
||||
int cx = COERCE_DX[c];
|
||||
// check if our hypothetical new coordinates are empty
|
||||
if (isBlockEmpty(ox + cx, oy,
|
||||
ORIENT_WIDTHS[oidx], ORIENT_HEIGHTS[oidx])) {
|
||||
// Log.info(
|
||||
// "Block is empty [ox=" + ox + ", cx=" + cx +
|
||||
// ", oy=" + oy + ", oidx=" + oidx +
|
||||
// ", orient=" + DirectionUtil.toShortString(orient) +
|
||||
// ", owid=" + ORIENT_WIDTHS[oidx] +
|
||||
// ", ohei=" + ORIENT_HEIGHTS[oidx] + "].");
|
||||
return new int[] { orient, px + cx, py, 0 };
|
||||
}
|
||||
}
|
||||
|
||||
// if our piece is facing south and we're using radial
|
||||
// rotation then we need to try popping the piece up a row to
|
||||
// check for a fit
|
||||
if (canPopup && rtype == RADIAL_ROTATION && orient == SOUTH) {
|
||||
// check if our hypothetical new coordinates are empty
|
||||
if (isBlockEmpty(ox, oy - 1,
|
||||
ORIENT_WIDTHS[oidx], ORIENT_HEIGHTS[oidx])) {
|
||||
// Log.info(
|
||||
// "Popped-up block is empty [ox=" + ox +
|
||||
// ", oy=" + (oy - 1) + ", oidx=" + oidx +
|
||||
// ", orient=" + DirectionUtil.toShortString(orient) +
|
||||
// ", owid=" + ORIENT_WIDTHS[oidx] +
|
||||
// ", ohei=" + ORIENT_HEIGHTS[oidx] +
|
||||
// ", bhei=" + _bhei + "].");
|
||||
return new int[] { orient, px, py - 1, 1 };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this should never happen since even in the most tightly
|
||||
// constrained case where the block is entirely surrounded by
|
||||
// other pieces there are always two valid orientations.
|
||||
Log.warning("**** We're horked and couldn't rotate at all!");
|
||||
// System.exit(0);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Point} object containing the coordinates to place
|
||||
* the bottom-left of the given block at after moving it the given
|
||||
* distance on the x- and y-axes, or <code>null</code> if the move is
|
||||
* not valid. Note that only the final block position is checked.
|
||||
*
|
||||
* @param col the leftmost column of the block.
|
||||
* @param row the bottommost row of the block.
|
||||
* @param wid the width of the block.
|
||||
* @param hei the height of the block.
|
||||
* @param dx the distance to move the block in columns.
|
||||
* @param dy the distance to move the block in rows.
|
||||
* @param pctdone the percentage of the inter-block distance that the
|
||||
* piece has fallen thus far.
|
||||
*/
|
||||
public Point getForgivingMove (
|
||||
int col, int row, int wid, int hei, int dx, int dy, float pctdone)
|
||||
{
|
||||
// try placing the block in the desired position and, failing
|
||||
// that, at the same horizontal position but one row farther down
|
||||
int xpos = col + dx, ypos = row + dy;
|
||||
|
||||
// if we're above the halfway mark, we check our current neighbors
|
||||
// to see if we can move there; if we're below the halfway mark we
|
||||
// check the next row down
|
||||
if (pctdone >= 0.5) {
|
||||
ypos += 1;
|
||||
}
|
||||
|
||||
// if the block we wish to occupy is empty, we're all good
|
||||
return (isBlockEmpty(xpos, ypos, wid, hei)) ?
|
||||
new Point(xpos, row + dy) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the given array with the column levels for this board.
|
||||
*/
|
||||
public void getColumnLevels (byte[] columns)
|
||||
{
|
||||
int bwid = getWidth(), bhei = getHeight();
|
||||
for (int col = 0; col < bwid; col++) {
|
||||
int dist = getDropDistance(col, -1);
|
||||
columns[col] = (byte)(bhei - dist);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the {@link DropControllerDelegate} when it's time to
|
||||
* apply a rising row of pieces to the board. Shifts all of the
|
||||
* pieces in the given board up one row and places the given row of
|
||||
* pieces at the bottom of the board.
|
||||
*/
|
||||
public void applyRisingPieces (int[] pieces)
|
||||
{
|
||||
// shift all pieces up one row
|
||||
int end = _bhei - 1;
|
||||
for (int yy = 0; yy < end; yy++) {
|
||||
for (int xx = 0; xx < _bwid; xx++) {
|
||||
setPiece(xx, yy, getPiece(xx, yy + 1));
|
||||
}
|
||||
}
|
||||
|
||||
// apply the row pieces to the board
|
||||
int ypos = _bhei - 1;
|
||||
for (int xx = 0; xx < _bwid; xx++) {
|
||||
setPiece(xx, ypos, pieces[xx]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the specified row (which count down, with zero at
|
||||
* the top of the board) contains any pieces.
|
||||
*
|
||||
* @param row the row to check for pieces.
|
||||
* @param blankPiece the blank piece value, non-instances of which
|
||||
* will be sought.
|
||||
*/
|
||||
public boolean rowContainsPieces (int row, int blankPiece)
|
||||
{
|
||||
for (int x = 0; x < _bwid; x++) {
|
||||
if (getPiece(x, row) != blankPiece) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the board contents with the given piece.
|
||||
*/
|
||||
public void fill (int piece)
|
||||
{
|
||||
Arrays.fill(_board, (int)piece);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the piece at the given coordinates.
|
||||
*
|
||||
* @return true if the piece was set, false if it was invalid.
|
||||
*/
|
||||
public boolean setPiece (int col, int row, int piece)
|
||||
{
|
||||
if (col >= 0 && row >= 0 && col < _bwid && row < _bhei) {
|
||||
_board[(row*_bwid) + col] = (int)piece;
|
||||
return true;
|
||||
|
||||
} else {
|
||||
Log.warning("Attempt to set piece outside board bounds " +
|
||||
"[col=" + col + ", row=" + row + ", p=" + piece + "].");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pieces within the specified rectangle to the given piece.
|
||||
*/
|
||||
public void setRect (int x, int y, int width, int height, int piece)
|
||||
{
|
||||
for (int yy = y; yy > (y - height); yy--) {
|
||||
for (int xx = x; xx < (x + width); xx++) {
|
||||
setPiece(xx, yy, piece);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pieces in the given board segment to the specified piece.
|
||||
*
|
||||
* @param dir the direction of the segment; one of {@link #HORIZONTAL}
|
||||
* or {@link #VERTICAL}.
|
||||
* @param col the starting column of the segment.
|
||||
* @param row the starting row of the segment.
|
||||
* @param len the length of the segment in pieces.
|
||||
* @param piece the piece to set in the segment.
|
||||
*
|
||||
* @return false if the segment was only partially applied because
|
||||
* some pieces were outside the bounds of the board, true if it was
|
||||
* completely applied.
|
||||
*/
|
||||
public boolean setSegment (int dir, int col, int row, int len, int piece)
|
||||
{
|
||||
_setPieceOp.init(piece);
|
||||
applyOp(dir, col, row, len, _setPieceOp);
|
||||
return !_setPieceOp.getError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pieces in the given board segment to the specified pieces.
|
||||
*
|
||||
* @param dir the direction of the segment; one of {@link #HORIZONTAL}
|
||||
* or {@link #VERTICAL}.
|
||||
* @param col the starting column of the segment.
|
||||
* @param row the starting row of the segment.
|
||||
* @param pieces the pieces to set in the segment.
|
||||
*/
|
||||
public void setSegment (int dir, int col, int row, int[] pieces)
|
||||
{
|
||||
_setSegmentOp.init(dir, pieces);
|
||||
applyOp(dir, col, row, pieces.length, _setSegmentOp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a specified {@link PieceOperation} to all pieces in the
|
||||
* specified row or column starting at the specified coordinates and
|
||||
* spanning the remainder of the row or column (depending on the
|
||||
* application direction) in the board.
|
||||
*
|
||||
* @param dir the direction to iterate in; one of {@link #HORIZONTAL}
|
||||
* or {@link #VERTICAL}.
|
||||
* @param col the starting column of the segment.
|
||||
* @param row the starting row of the segment.
|
||||
* @param op the piece operation to apply to each piece.
|
||||
*/
|
||||
public void applyOp (int dir, int col, int row, PieceOperation op)
|
||||
{
|
||||
int len = (dir == HORIZONTAL) ? _bwid - col : row + 1;
|
||||
applyOp(dir, col, row, len, op);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a specified {@link PieceOperation} to all pieces in a row
|
||||
* or column segment starting at the specified coordinates and of the
|
||||
* specified length in the board.
|
||||
*
|
||||
* @param dir the direction to iterate in; one of {@link #HORIZONTAL}
|
||||
* or {@link #VERTICAL}.
|
||||
* @param col the starting leftmost column of the segment.
|
||||
* @param row the starting bottommost row of the segment.
|
||||
* @param len the number of pieces in the segment.
|
||||
* @param op the piece operation to apply to each piece.
|
||||
*/
|
||||
public void applyOp (int dir, int col, int row, int len, PieceOperation op)
|
||||
{
|
||||
if (dir == HORIZONTAL) {
|
||||
int end = Math.min(col + len, _bwid);
|
||||
for (int ii = col; ii < end; ii++) {
|
||||
if (!op.execute(this, ii, row)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
int end = Math.max(row - len, -1);
|
||||
for (int ii = row; ii > end; ii--) {
|
||||
if (!op.execute(this, col, ii)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a specified {@link PieceOperation} to the specified piece
|
||||
* in the board.
|
||||
*
|
||||
* @param col the column of the piece.
|
||||
* @param row the row of the piece.
|
||||
* @param op the piece operation to apply to the piece.
|
||||
*/
|
||||
public void applyOp (int col, int row, PieceOperation op)
|
||||
{
|
||||
op.execute(this, col, row);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void dump ()
|
||||
{
|
||||
dumpAndCompare(null);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void dumpAndCompare (Board other)
|
||||
{
|
||||
if (other != null && !(other instanceof DropBoard)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Can't compare drop board to non-drop-board.");
|
||||
}
|
||||
|
||||
DropBoard dother = (DropBoard)other;
|
||||
int padwid = getPadWidth();
|
||||
if (other != null) {
|
||||
// padwid = (padwid * 2) + 1;
|
||||
padwid *= 2;
|
||||
}
|
||||
|
||||
for (int y = 0; y < _bhei; y++) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (int x = 0; x < _bwid; x++) {
|
||||
int piece = getPiece(x, y);
|
||||
String str = formatPiece(piece);
|
||||
if (dother != null) {
|
||||
int opiece = dother.getPiece(x, y);
|
||||
if (opiece != piece) {
|
||||
str += "|" + formatPiece(opiece);
|
||||
}
|
||||
}
|
||||
buf.append(StringUtils.rightPad(str, padwid));
|
||||
}
|
||||
System.err.println(buf.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns a string representation of this instance. */
|
||||
public String toString ()
|
||||
{
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("[wid=").append(_bwid);
|
||||
buf.append(", hei=").append(_bhei);
|
||||
return buf.append("]").toString();
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public boolean equals (Board other)
|
||||
{
|
||||
// make sure we're comparing the same class type
|
||||
if (!this.getClass().getName().equals(other.getClass().getName())) {
|
||||
throw new IllegalArgumentException(
|
||||
"Can't compare board of different class types " +
|
||||
"[src=" + this.getClass().getName() +
|
||||
", other=" + other.getClass().getName() + "].");
|
||||
}
|
||||
|
||||
// we're certainly not equal if our dimensions differ
|
||||
DropBoard dother = (DropBoard)other;
|
||||
if (dother.getWidth() != _bwid ||
|
||||
dother.getHeight() != _bhei) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check each board piece
|
||||
for (int xx = 0; xx < _bwid; xx++) {
|
||||
for (int yy = 0; yy < _bhei; yy++) {
|
||||
if (getPiece(xx, yy) != dother.getPiece(xx, yy)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we're equal
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given coordinates are within the board bounds.
|
||||
*/
|
||||
public boolean isValidPosition (int x, int y)
|
||||
{
|
||||
return (x >= 0 &&
|
||||
y >= 0 &&
|
||||
x < _bwid &&
|
||||
y < _bhei);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bounds of this board. Note that a single rectangle is
|
||||
* re-used internally and so the caller should not modify the
|
||||
* returned rectangle.
|
||||
*/
|
||||
public Rectangle getBounds ()
|
||||
{
|
||||
if (_bounds == null) {
|
||||
_bounds = new Rectangle(0, 0, _bwid, _bhei);
|
||||
}
|
||||
return _bounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the board in pieces.
|
||||
*/
|
||||
public int size ()
|
||||
{
|
||||
return (_bwid*_bhei);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the contents of this board directly into the supplied board,
|
||||
* overwriting the destination board in its entirety.
|
||||
*/
|
||||
public void copyInto (DropBoard board)
|
||||
{
|
||||
// make sure the target board is a valid target
|
||||
if (board.getWidth() != _bwid || board.getHeight() != _bhei) {
|
||||
Log.warning("Can't copy board into destination board with " +
|
||||
"different dimensions [src=" + this +
|
||||
", dest=" + board + "].");
|
||||
return;
|
||||
}
|
||||
|
||||
// copy our pieces directly into the board, avoiding any unsightly
|
||||
// object allocation which is largely the point of this method,
|
||||
// after all.
|
||||
int[] dest = ((DropBoard)board).getBoard();
|
||||
System.arraycopy(_board, 0, dest, 0, (_bwid*_bhei));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw board data associated with this board. One
|
||||
* shouldn't fiddle about with this unless one knows what one is
|
||||
* doing.
|
||||
*/
|
||||
public int[] getBoard ()
|
||||
{
|
||||
return _board;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the board data and board dimensions.
|
||||
*/
|
||||
public void setBoard (int[] board, int bwid, int bhei)
|
||||
{
|
||||
_board = board;
|
||||
_bwid = bwid;
|
||||
_bhei = bhei;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the board pieces.
|
||||
*/
|
||||
public void setBoard (int[] board)
|
||||
{
|
||||
int size = (_bwid*_bhei);
|
||||
if (board.length < size) {
|
||||
Log.warning("Attempt to set board with invalid data size " +
|
||||
"[len=" + board.length + ", expected=" + size + "].");
|
||||
return;
|
||||
}
|
||||
|
||||
_board = board;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public Object clone ()
|
||||
{
|
||||
DropBoard board = (DropBoard)super.clone();
|
||||
board._board = (int[])_board.clone();
|
||||
return board;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of characters to which a single piece should be
|
||||
* padded when dumping the board for debugging purposes.
|
||||
*/
|
||||
protected int getPadWidth ()
|
||||
{
|
||||
return DEFAULT_PAD_WIDTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the given piece for use when
|
||||
* dumping the board.
|
||||
*/
|
||||
protected String formatPiece (int piece)
|
||||
{
|
||||
return (piece == PIECE_NONE) ? "." : String.valueOf(piece);
|
||||
}
|
||||
|
||||
/** An operation that sets the pieces in a board segment to a
|
||||
* specified array of pieces. */
|
||||
protected static class SetSegmentOperation implements PieceOperation
|
||||
{
|
||||
/**
|
||||
* Sets the array of pieces to be placed in the board segment.
|
||||
*/
|
||||
public void init (int dir, int[] pieces)
|
||||
{
|
||||
_dir = dir;
|
||||
_pieces = pieces;
|
||||
_idx = (dir == HORIZONTAL) ? _pieces.length - 1 : 0;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public boolean execute (DropBoard board, int col, int row)
|
||||
{
|
||||
if (_dir == HORIZONTAL) {
|
||||
board.setPiece(col, row, _pieces[_idx--]);
|
||||
} else {
|
||||
board.setPiece(col, row, _pieces[_idx++]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** The orientation in which the pieces are to be placed. */
|
||||
protected int _dir;
|
||||
|
||||
/** The current piece index. */
|
||||
protected int _idx;
|
||||
|
||||
/** The pieces to set in the board. */
|
||||
protected int[] _pieces;
|
||||
}
|
||||
|
||||
/** An operation that sets all pieces to a specified piece. */
|
||||
protected static class SetPieceOperation implements PieceOperation
|
||||
{
|
||||
/**
|
||||
* Sets the piece to be placed in the board segment.
|
||||
*/
|
||||
public void init (int piece)
|
||||
{
|
||||
_piece = piece;
|
||||
_error = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we attempted to set a piece outside the bounds
|
||||
* of the board during the course of our operation.
|
||||
*/
|
||||
public boolean getError ()
|
||||
{
|
||||
return _error;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public boolean execute (DropBoard board, int col, int row)
|
||||
{
|
||||
if (!board.setPiece(col, row, _piece)) {
|
||||
_error = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** The piece to set in the board. */
|
||||
protected int _piece;
|
||||
|
||||
/** Set to true if an error occurred setting a piece. */
|
||||
protected boolean _error;
|
||||
}
|
||||
|
||||
/** The board data. */
|
||||
protected int[] _board;
|
||||
|
||||
/** The board dimensions in pieces. */
|
||||
protected int _bwid, _bhei;
|
||||
|
||||
/** The bounds of this board. */
|
||||
protected transient Rectangle _bounds;
|
||||
|
||||
// used to reconfigure the block when rotating it
|
||||
protected static final int[][][] ROTATE_DX = {
|
||||
// W N E S W N E S
|
||||
{{ 0, 0, 0, 0 }, { 0, 0, 0, 0 }}, // RADIAL
|
||||
{{ -1, 1, 0, 0 }, { -1, 0, 0, 1 }}, // INPLACE
|
||||
// CCW CW
|
||||
};
|
||||
|
||||
// used to reconfigure the block when rotating it
|
||||
protected static final int[][][] ROTATE_DY = {
|
||||
// W N E S W N E S
|
||||
{{ 0, 0, 0, 0 }, { 0, 0, 0, 0 }}, // RADIAL
|
||||
{{ -1, 0, 0, 1 }, { 0, 0, -1, 1 }}, // INPLACE
|
||||
// CCW CW
|
||||
};
|
||||
|
||||
// used to compute the bounds of the isBlockEmpty() block based on the
|
||||
// drop block's orientation and "root" block position
|
||||
protected static final int[] ORIENT_WIDTHS = { 2, 1, 2, 1 };
|
||||
protected static final int[] ORIENT_HEIGHTS = { 1, 2, 1, 2 };
|
||||
|
||||
// used to compute the origin of the isBlockEmpty() block based on the
|
||||
// drop block's orientation and "root" block position
|
||||
protected static final int[] ORIENT_ORIGIN_DX = { -1, 0, 0, 0 };
|
||||
protected static final int[] ORIENT_ORIGIN_DY = { 0, 0, 0, 1 };
|
||||
|
||||
// used to coerce the block when rotating either a space to the left
|
||||
// or right (or not at all)
|
||||
protected static final int[] COERCE_DX = { 0, 1, -1 };
|
||||
|
||||
/** The operation used to set the pieces in a board segment. */
|
||||
protected static final SetSegmentOperation _setSegmentOp =
|
||||
new SetSegmentOperation();
|
||||
|
||||
/** The operation used to set a piece in a board segment. */
|
||||
protected static final SetPieceOperation _setPieceOp =
|
||||
new SetPieceOperation();
|
||||
|
||||
/** The number of characters to which each board piece should be
|
||||
* padded when outputting for debug purposes. */
|
||||
protected static final int DEFAULT_PAD_WIDTH = 3;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// $Id: DropBoardSummary.java 3495 2005-04-15 21:44:36Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.drop.data;
|
||||
|
||||
import com.threerings.puzzle.data.Board;
|
||||
import com.threerings.puzzle.data.BoardSummary;
|
||||
|
||||
/**
|
||||
* Provides a summary of a {@link DropBoard}.
|
||||
*/
|
||||
public class DropBoardSummary extends BoardSummary
|
||||
{
|
||||
/** The row levels for each column. */
|
||||
public byte[] columns;
|
||||
|
||||
/**
|
||||
* Constructs an empty drop board summary for use when un-serializing.
|
||||
*/
|
||||
public DropBoardSummary ()
|
||||
{
|
||||
// nothing for now
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a drop board summary that retrieves board information
|
||||
* from the supplied board when summarizing.
|
||||
*/
|
||||
public DropBoardSummary (Board board)
|
||||
{
|
||||
super(board);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the column number of the column within the given column
|
||||
* range that contains the most pieces.
|
||||
*/
|
||||
public int getHighestColumn (int startx, int endx)
|
||||
{
|
||||
byte value = columns[startx];
|
||||
int idx = startx;
|
||||
for (int xx = startx + 1; xx <= endx; xx++) {
|
||||
if (columns[xx] > value) {
|
||||
value = columns[xx];
|
||||
idx = xx;
|
||||
}
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void setBoard (Board board)
|
||||
{
|
||||
_dboard = (DropBoard)board;
|
||||
// create the columns array
|
||||
columns = new byte[_dboard.getWidth()];
|
||||
|
||||
super.setBoard(board);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void summarize ()
|
||||
{
|
||||
// update the board column levels
|
||||
_dboard.getColumnLevels(columns);
|
||||
}
|
||||
|
||||
/** The drop board we're summarizing. */
|
||||
protected transient DropBoard _dboard;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// $Id: DropCodes.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.drop.data;
|
||||
|
||||
import com.threerings.puzzle.data.PuzzleGameCodes;
|
||||
|
||||
/**
|
||||
* Contains codes used by the drop game services.
|
||||
*/
|
||||
public interface DropCodes extends PuzzleGameCodes
|
||||
{
|
||||
/** The message bundle identifier for drop puzzle messages. */
|
||||
public static final String DROP_MESSAGE_BUNDLE = "puzzle.drop";
|
||||
|
||||
/** The name of the control stream that provides drop pieces. */
|
||||
public static final String DROP_STREAM = "drop";
|
||||
|
||||
/** The name of the control stream that provides rise pieces. */
|
||||
public static final String RISE_STREAM = "rise";
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// $Id: DropConfig.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.drop.data;
|
||||
|
||||
/**
|
||||
* Provides access to the configuration information for a drop puzzle
|
||||
* game.
|
||||
*/
|
||||
public interface DropConfig
|
||||
{
|
||||
/** Returns the board width in pieces. */
|
||||
public int getBoardWidth ();
|
||||
|
||||
/** Returns the board height in pieces. */
|
||||
public int getBoardHeight ();
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// $Id: DropLogic.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.drop.data;
|
||||
|
||||
/**
|
||||
* Describes the features and configuration desired for a given drop
|
||||
* puzzle game.
|
||||
*/
|
||||
public interface DropLogic
|
||||
{
|
||||
/**
|
||||
* Returns whether the puzzle game would like to make use of the
|
||||
* manipulable block dropping functionality.
|
||||
*/
|
||||
public boolean useBlockDropping ();
|
||||
|
||||
/**
|
||||
* Returns whether the puzzle game would like to make use of the
|
||||
* rising board functionality.
|
||||
*/
|
||||
public boolean useBoardRising ();
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// $Id: DropPieceCodes.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.drop.data;
|
||||
|
||||
import com.threerings.util.DirectionCodes;
|
||||
|
||||
/**
|
||||
* The drop piece codes interface contains constants common to the drop
|
||||
* game package.
|
||||
*/
|
||||
public interface DropPieceCodes extends DirectionCodes
|
||||
{
|
||||
/** The piece constant denoting an empty board piece. */
|
||||
public static final byte PIECE_NONE = -1;
|
||||
|
||||
/** The number of pieces in a drop block. */
|
||||
public static final int DROP_BLOCK_PIECE_COUNT = 2;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// $Id: SegmentInfo.java 3310 2005-01-24 23:08:21Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.drop.data;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
import com.threerings.util.DirectionCodes;
|
||||
|
||||
/**
|
||||
* Describes a segment of pieces in a {@link DropBoard}.
|
||||
*/
|
||||
public class SegmentInfo
|
||||
{
|
||||
/** The segment's direction; one of {@link DirectionCodes#HORIZONTAL}
|
||||
* or {@link DirectionCodes#VERTICAL}. */
|
||||
public int dir;
|
||||
|
||||
/** The segment's lower-left board coordinates. */
|
||||
public int x, y;
|
||||
|
||||
/** The segment's length in pieces. */
|
||||
public int len;
|
||||
|
||||
/**
|
||||
* Constructs a segment info object.
|
||||
*/
|
||||
public SegmentInfo (int dir, int x, int y, int len)
|
||||
{
|
||||
this.dir = dir;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.len = len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this instance.
|
||||
*/
|
||||
public String toString ()
|
||||
{
|
||||
return StringUtil.fieldsToString(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
//
|
||||
// $Id: DropManagerDelegate.java 3777 2005-12-07 19:19:11Z mjohnson $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.drop.server;
|
||||
|
||||
import com.threerings.crowd.data.PlaceConfig;
|
||||
import com.threerings.crowd.data.PlaceObject;
|
||||
|
||||
import com.threerings.puzzle.Log;
|
||||
import com.threerings.puzzle.data.Board;
|
||||
import com.threerings.puzzle.data.PuzzleCodes;
|
||||
import com.threerings.puzzle.server.PuzzleManager;
|
||||
import com.threerings.puzzle.server.PuzzleManagerDelegate;
|
||||
|
||||
import com.threerings.puzzle.drop.data.DropBoard;
|
||||
import com.threerings.puzzle.drop.data.DropCodes;
|
||||
import com.threerings.puzzle.drop.data.DropConfig;
|
||||
import com.threerings.puzzle.drop.data.DropLogic;
|
||||
import com.threerings.puzzle.drop.util.PieceDropLogic;
|
||||
import com.threerings.puzzle.drop.util.PieceDropper;
|
||||
|
||||
/**
|
||||
* Provides the necessary support for a puzzle game that involves a
|
||||
* two-dimensional board containing pieces, with new pieces either falling
|
||||
* into the board as a "drop block", or rising into the bottom of the
|
||||
* board in new piece rows, groups of blocks can be "broken" and garbage
|
||||
* can be sent to other players' boards as a result. This is implemented
|
||||
* as a delegate so that the natural hierarchy need not be twisted to
|
||||
* differentiate between puzzles that use piece dropping and those that
|
||||
* don't. Because we have need to structure our hierarchy around things
|
||||
* like whether a puzzle is a duty puzzle, this becomes necessary.
|
||||
*
|
||||
* <p> A puzzle game using these services will then need to extend this
|
||||
* delegate, implementing the necessary methods to customize it for the
|
||||
* particulars of their game and then register it with their game manager
|
||||
* via {@link PuzzleManager#addDelegate}.
|
||||
*
|
||||
* <p> It also keeps track of, for each player, board level information,
|
||||
* and player game status. Miscellaneous utility routines are provided
|
||||
* for checking things like whether the game is over, whether a player is
|
||||
* still active in the game, and so forth.
|
||||
*
|
||||
* <p> Derived classes are likely to want to override {@link
|
||||
* #getPieceDropLogic}.
|
||||
*/
|
||||
public abstract class DropManagerDelegate extends PuzzleManagerDelegate
|
||||
implements PuzzleCodes, DropCodes
|
||||
{
|
||||
/**
|
||||
* Provides the delegate with a reference to the manager for which it
|
||||
* is delegating as well as the logic object that it uses to determine
|
||||
* how to manage the drop puzzle.
|
||||
*/
|
||||
public DropManagerDelegate (PuzzleManager puzmgr, DropLogic logic)
|
||||
{
|
||||
super(puzmgr);
|
||||
|
||||
// configure the game-specific settings
|
||||
_usedrop = logic.useBlockDropping();
|
||||
_userise = logic.useBoardRising();
|
||||
if (_usedrop && _userise) {
|
||||
Log.warning("Can't use dropping blocks and board rising "+
|
||||
"functionality simultaneously in a drop puzzle game! " +
|
||||
"Falling back to straight dropping.");
|
||||
_userise = false;
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void didInit (PlaceConfig config)
|
||||
{
|
||||
_dconfig = (DropConfig)config;
|
||||
|
||||
// save things off
|
||||
_bwid = _dconfig.getBoardWidth();
|
||||
_bhei = _dconfig.getBoardHeight();
|
||||
|
||||
super.didInit(config);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void didStartup (PlaceObject plobj)
|
||||
{
|
||||
super.didStartup(plobj);
|
||||
|
||||
// initialize the drop board array
|
||||
_dboards = new DropBoard[_puzmgr.getPlayerCount()];
|
||||
|
||||
// create the piece dropper if appropriate
|
||||
PieceDropLogic pdl = getPieceDropLogic();
|
||||
if (pdl != null) {
|
||||
_dropper = new PieceDropper(pdl);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void gameWillStart ()
|
||||
{
|
||||
super.gameWillStart();
|
||||
|
||||
// get casted references to all player drop boards
|
||||
Board[] board = _puzmgr.getBoards();
|
||||
for (int ii = 0; ii < _puzmgr.getPlayerCount(); ii++) {
|
||||
_dboards[ii] = (DropBoard)board[ii];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops any pieces that need dropping on the given player's board and
|
||||
* returns whether any pieces were dropped.
|
||||
*/
|
||||
protected boolean dropPieces (DropBoard board)
|
||||
{
|
||||
return (_dropper.dropPieces(board, null) > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the piece drop logic used to drop any pieces that need
|
||||
* dropping in the board.
|
||||
*/
|
||||
protected PieceDropLogic getPieceDropLogic ()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should be called by derived classes whenever the player
|
||||
* successfully places a drop block.
|
||||
*/
|
||||
protected void placedBlock (int pidx)
|
||||
{
|
||||
}
|
||||
|
||||
/** The drop game board for each player. */
|
||||
protected DropBoard[] _dboards;
|
||||
|
||||
/** The drop game config object. */
|
||||
protected DropConfig _dconfig;
|
||||
|
||||
/** Whether the game is using drop block functionality. */
|
||||
protected boolean _usedrop;
|
||||
|
||||
/** Whether the game is using board rising functionality. */
|
||||
protected boolean _userise;
|
||||
|
||||
/** The board dimensions in pieces. */
|
||||
protected int _bwid, _bhei;
|
||||
|
||||
/** The piece dropper used to drop pieces in the board if the puzzle
|
||||
* chooses to make use of piece dropping functionality. */
|
||||
protected PieceDropper _dropper;
|
||||
|
||||
/** Used to limit the maximum number of board update loops permitted
|
||||
* before assuming something's gone horribly awry and aborting. */
|
||||
protected static final int MAX_UPDATE_LOOPS = 100;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// $Id: DropBoardUtil.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.drop.util;
|
||||
|
||||
import com.threerings.util.DirectionCodes;
|
||||
|
||||
public class DropBoardUtil
|
||||
implements DirectionCodes
|
||||
{
|
||||
/**
|
||||
* Returns the orientation resulting from rotating the block in the
|
||||
* given direction the specified number of times.
|
||||
*
|
||||
* @param orient the current orientation.
|
||||
* @param dir the direction to rotate in; one of <code>CW</code> or
|
||||
* <code>CCW</code>.
|
||||
* @param count the number of rotations to perform.
|
||||
*
|
||||
* @return the rotated orientation.
|
||||
*/
|
||||
public static int getRotatedOrientation (int orient, int dir, int count)
|
||||
{
|
||||
for (int ii = 0; ii < (count % 4); ii++) {
|
||||
orient = getRotatedOrientation(orient, dir);
|
||||
}
|
||||
return orient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the orientation resulting from rotating the block in
|
||||
* the given direction.
|
||||
*
|
||||
* @param orient the current orientation.
|
||||
* @param dir the direction to rotate in; one of <code>CW</code> or
|
||||
* <code>CCW</code>.
|
||||
*
|
||||
* @return the rotated orientation.
|
||||
*/
|
||||
public static int getRotatedOrientation (int orient, int dir)
|
||||
{
|
||||
return (orient + ((dir == CW) ? 2 : 6)) % DIRECTION_COUNT;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// $Id: DropGameUtil.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.drop.util;
|
||||
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import com.threerings.util.KeyTranslatorImpl;
|
||||
|
||||
import com.threerings.puzzle.drop.client.DropControllerDelegate;
|
||||
import com.threerings.puzzle.util.PuzzleGameUtil;
|
||||
|
||||
/**
|
||||
* Drop puzzle game related utilities.
|
||||
*/
|
||||
public class DropGameUtil
|
||||
{
|
||||
/**
|
||||
* Returns a key translator configured with mappings suitable for a
|
||||
* drop puzzle game.
|
||||
*/
|
||||
public static KeyTranslatorImpl getKeyTranslator ()
|
||||
{
|
||||
// start with the standard puzzle key mappings
|
||||
KeyTranslatorImpl xlate = PuzzleGameUtil.getKeyTranslator();
|
||||
|
||||
// add all press key mappings
|
||||
xlate.addPressCommand(KeyEvent.VK_LEFT,
|
||||
DropControllerDelegate.MOVE_BLOCK_LEFT,
|
||||
MOVE_RATE, MOVE_DELAY);
|
||||
xlate.addPressCommand(KeyEvent.VK_RIGHT,
|
||||
DropControllerDelegate.MOVE_BLOCK_RIGHT,
|
||||
MOVE_RATE, MOVE_DELAY);
|
||||
xlate.addPressCommand(KeyEvent.VK_UP,
|
||||
DropControllerDelegate.ROTATE_BLOCK_CCW, 0);
|
||||
xlate.addPressCommand(KeyEvent.VK_DOWN,
|
||||
DropControllerDelegate.ROTATE_BLOCK_CW, 0);
|
||||
xlate.addPressCommand(KeyEvent.VK_SPACE,
|
||||
DropControllerDelegate.START_DROP_BLOCK, 0);
|
||||
|
||||
// add all release key mappings
|
||||
xlate.addReleaseCommand(KeyEvent.VK_SPACE,
|
||||
DropControllerDelegate.END_DROP_BLOCK);
|
||||
|
||||
return xlate;
|
||||
}
|
||||
|
||||
/** The move key repeat rate in moves per second. */
|
||||
protected static final int MOVE_RATE = 7;
|
||||
|
||||
/** The delay in milliseconds before the move keys begin to repeat. */
|
||||
protected static final long MOVE_DELAY = 300L;
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
//
|
||||
// $Id: PieceDestroyer.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.drop.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.threerings.puzzle.drop.data.DropBoard;
|
||||
import com.threerings.puzzle.drop.data.DropBoard.PieceOperation;
|
||||
import com.threerings.puzzle.drop.data.DropPieceCodes;
|
||||
import com.threerings.puzzle.drop.data.SegmentInfo;
|
||||
|
||||
/**
|
||||
* Handles destroying contiguous piece segments in a drop board.
|
||||
*/
|
||||
public class PieceDestroyer
|
||||
implements DropPieceCodes
|
||||
{
|
||||
/**
|
||||
* An interface to be implemented by specific puzzles to detail the
|
||||
* parameters and methodology by which pieces are destroyed in the
|
||||
* puzzle board.
|
||||
*/
|
||||
public interface DestroyLogic
|
||||
{
|
||||
/**
|
||||
* Returns the minimum length of a contiguously piece segment that
|
||||
* should be destroyed.
|
||||
*/
|
||||
public int getMinimumLength ();
|
||||
|
||||
/**
|
||||
* Returns whether piece <code>a</code> is equivalent to piece
|
||||
* <code>b</code> for the purposes of including it in a contiguous
|
||||
* piece segment to be destroyed.
|
||||
*/
|
||||
public boolean isEquivalent (int a, int b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a piece destroyer that destroys pieces as specified by
|
||||
* the supplied destroy logic.
|
||||
*/
|
||||
public PieceDestroyer (DestroyLogic logic)
|
||||
{
|
||||
_logic = logic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys all pieces in the given board that are in contiguous rows
|
||||
* or columns of pieces, returning a list of {@link SegmentInfo}
|
||||
* objects detailing the destroyed piece segments. Note that a single
|
||||
* list is used internally to gather the segment info, and so callers
|
||||
* that care to modify the list should create their own copy; also,
|
||||
* the pieces in the segments may overlap, i.e., two segments may
|
||||
* contain the same piece.
|
||||
*/
|
||||
public List destroyPieces (DropBoard board, PieceOperation destroyOp)
|
||||
{
|
||||
// find all horizontally-oriented destroyed segments
|
||||
int bwid = board.getWidth(), bhei = board.getHeight();
|
||||
_destroyed.clear();
|
||||
int end = bwid - _logic.getMinimumLength() + 1;
|
||||
for (int yy = (bhei - 1); yy >= 0; yy--) {
|
||||
int xx = 0;
|
||||
while (xx < end) {
|
||||
xx += findSegment(board, HORIZONTAL, xx, yy);
|
||||
}
|
||||
}
|
||||
|
||||
// find all vertically-oriented destroyed segments
|
||||
end = _logic.getMinimumLength() - 2;
|
||||
for (int xx = 0; xx < bwid; xx++) {
|
||||
int yy = bhei - 1;
|
||||
while (yy > end) {
|
||||
yy -= findSegment(board, VERTICAL, xx, yy);
|
||||
}
|
||||
}
|
||||
|
||||
// destroy the pieces
|
||||
int size = _destroyed.size();
|
||||
for (int ii = 0; ii < size; ii++) {
|
||||
SegmentInfo si = (SegmentInfo)_destroyed.get(ii);
|
||||
board.applyOp(si.dir, si.x, si.y, si.len, destroyOp);
|
||||
}
|
||||
|
||||
return _destroyed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a contiguously colored piece segment with the
|
||||
* specified orientation and root coordinates in the supplied board
|
||||
* and returns the length of the segment traversed.
|
||||
*/
|
||||
protected int findSegment (DropBoard board, int dir, int x, int y)
|
||||
{
|
||||
_lengthOp.reset();
|
||||
board.applyOp(dir, x, y, _lengthOp);
|
||||
int len = _lengthOp.getLength();
|
||||
if (len >= _logic.getMinimumLength()) {
|
||||
_destroyed.add(new SegmentInfo(dir, x, y, len));
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* A piece operation that calculates the length of the contiguous
|
||||
* piece segment to which it is applied.
|
||||
*/
|
||||
protected class SegmentLengthOperation
|
||||
implements PieceOperation
|
||||
{
|
||||
/**
|
||||
* Resets the operation for application to a new piece segment.
|
||||
*/
|
||||
public void reset ()
|
||||
{
|
||||
_len = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of the contiguous piece segment.
|
||||
*/
|
||||
public int getLength ()
|
||||
{
|
||||
return _len;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public boolean execute (DropBoard board, int col, int row)
|
||||
{
|
||||
int piece = board.getPiece(col, row);
|
||||
if (_len == 0) {
|
||||
_len = 1;
|
||||
_piece = piece;
|
||||
return (piece != PIECE_NONE);
|
||||
|
||||
} else if (_logic.isEquivalent(piece, _piece)) {
|
||||
_len++;
|
||||
return true;
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** The root segment piece. */
|
||||
protected int _piece;
|
||||
|
||||
/** The segment length in pieces. */
|
||||
protected int _len;
|
||||
}
|
||||
|
||||
/** The puzzle-specific destroy logic with which we do our business. */
|
||||
protected DestroyLogic _logic;
|
||||
|
||||
/** The piece operation used to determine segment length. */
|
||||
protected SegmentLengthOperation _lengthOp = new SegmentLengthOperation();
|
||||
|
||||
/** The list of destroyed piece segments. */
|
||||
protected ArrayList _destroyed = new ArrayList();
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
//
|
||||
// $Id: PieceDropLogic.java 3310 2005-01-24 23:08:21Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.drop.util;
|
||||
|
||||
import com.threerings.util.DirectionCodes;
|
||||
|
||||
import com.threerings.puzzle.drop.data.DropBoard;
|
||||
|
||||
/**
|
||||
* An interface to be implemented by games that would like to be able to
|
||||
* drop their pieces during game play.
|
||||
*/
|
||||
public interface PieceDropLogic
|
||||
{
|
||||
/**
|
||||
* Should the board always be filled?
|
||||
*
|
||||
* @return false for normal behavior.
|
||||
*/
|
||||
public boolean boardAlwaysFilled ();
|
||||
|
||||
/**
|
||||
* Returns whether the given piece is potentially droppable.
|
||||
*/
|
||||
public boolean isDroppablePiece (int piece);
|
||||
|
||||
/**
|
||||
* Returns whether the given piece has constraints upon it that
|
||||
* impact its droppability.
|
||||
*/
|
||||
public boolean isConstrainedPiece (int piece);
|
||||
|
||||
/**
|
||||
* Returns whether the given piece terminates a column climb when
|
||||
* determining the height of a piece column to be dropped.
|
||||
*
|
||||
* @param allowConst whether to allow dropping constrained pieces
|
||||
* (though only in the first encountered constrained block.)
|
||||
* @param piece the piece to consider.
|
||||
* @param pre whether the climbability check is being performed
|
||||
* before the height is incremented, or after.
|
||||
*/
|
||||
public boolean isClimbablePiece (
|
||||
boolean allowConst, int piece, boolean pre);
|
||||
|
||||
/**
|
||||
* Returns the x-axis coordinate of the specified edge of the
|
||||
* given constrained piece.
|
||||
*
|
||||
* <p> TODO: This should go away once the sword and sail games
|
||||
* have standardized on WEST/EAST or BLOCK_LEFT/BLOCK_RIGHT to
|
||||
* reference block edges.
|
||||
*
|
||||
* @param board the board to search.
|
||||
* @param col the column of the constrained piece.
|
||||
* @param row the row of the constrained piece.
|
||||
* @param dir the edge direction to find; one of {@link
|
||||
* DirectionCodes#LEFT} or {@link DirectionCodes#RIGHT}.
|
||||
*/
|
||||
public int getConstrainedEdge (DropBoard board, int col, int row, int dir);
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
//
|
||||
// $Id: PieceDropper.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.drop.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.puzzle.Log;
|
||||
import com.threerings.puzzle.drop.data.DropBoard;
|
||||
import com.threerings.puzzle.drop.data.DropPieceCodes;
|
||||
|
||||
/**
|
||||
* Handles dropping pieces in a board.
|
||||
*/
|
||||
public class PieceDropper
|
||||
implements DropPieceCodes
|
||||
{
|
||||
/**
|
||||
* A class to hold information detailing the pieces to be dropped
|
||||
* in a particular column.
|
||||
*/
|
||||
public static class PieceDropInfo
|
||||
{
|
||||
/** The starting row of the bottom piece being dropped. */
|
||||
public int row;
|
||||
|
||||
/** The column number. */
|
||||
public int col;
|
||||
|
||||
/** The distance to drop the pieces. */
|
||||
public int dist;
|
||||
|
||||
/** The pieces to be dropped. */
|
||||
public int[] pieces;
|
||||
|
||||
/**
|
||||
* Constructs a piece drop info object.
|
||||
*/
|
||||
public PieceDropInfo (int col, int row, int dist)
|
||||
{
|
||||
this.col = col;
|
||||
this.row = row;
|
||||
this.dist = dist;
|
||||
}
|
||||
|
||||
/** Returns a string representation of this instance. */
|
||||
public String toString ()
|
||||
{
|
||||
return StringUtil.fieldsToString(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to inform a drop observer that a piece has been dropped.
|
||||
*/
|
||||
public static interface DropObserver
|
||||
{
|
||||
/** Indicates that the specified piece was dropped. */
|
||||
public void pieceDropped (int piece, int sx, int sy, int dx, int dy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a piece dropper that uses the supplied piece drop logic
|
||||
* to specialise itself for a particular puzzle.
|
||||
*/
|
||||
public PieceDropper (PieceDropLogic logic)
|
||||
{
|
||||
_logic = logic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Effects any drops possible on the supplied board (modifying the
|
||||
* board in the progress) and notifying the supplied drop observer of
|
||||
* those drops.
|
||||
*
|
||||
* @return the number of pieces dropped.
|
||||
*/
|
||||
public int dropPieces (DropBoard board, DropObserver drobs)
|
||||
{
|
||||
int dropped = 0, bhei = board.getHeight(), bwid = board.getWidth();
|
||||
for (int yy = bhei - 1; yy >= 0; yy--) {
|
||||
for (int xx = 0; xx < bwid; xx++) {
|
||||
dropped += dropPieces(board, xx, yy, drobs);
|
||||
}
|
||||
}
|
||||
|
||||
// if the board wants pieces to be dropped in to fill the gaps, do
|
||||
// that now
|
||||
if (_logic.boardAlwaysFilled()) {
|
||||
for (int xx = 0; xx < bwid; xx++) {
|
||||
int dist = board.getDropDistance(xx, -1);
|
||||
for (int ii = 0; ii < dist; ii++) {
|
||||
int yy = (-1 - ii);
|
||||
int piece = board.getNextPiece();
|
||||
if (piece != PIECE_NONE) {
|
||||
drop(board, piece, xx, yy, yy + dist, drobs);
|
||||
dropped++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dropped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes and effects the drop for the specified piece and any
|
||||
* associated attached pieces. The supplied observer is notified of
|
||||
* all drops.
|
||||
*/
|
||||
protected int dropPieces (
|
||||
DropBoard board, int xx, int yy, DropObserver drobs)
|
||||
{
|
||||
// skip empty or fixed pieces
|
||||
int piece = board.getPiece(xx, yy);
|
||||
if (!_logic.isDroppablePiece(piece)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dropped = 0;
|
||||
if (_logic.isConstrainedPiece(piece)) {
|
||||
// find out where this constrained block starts and ends
|
||||
int start = _logic.getConstrainedEdge(board, xx, yy, LEFT);
|
||||
int end = _logic.getConstrainedEdge(board, xx, yy, RIGHT);
|
||||
int bwid = board.getWidth();
|
||||
if (start < 0 || end >= bwid) {
|
||||
Log.warning("Board reported bogus constrained edge " +
|
||||
"[x=" + xx + ", y=" + yy +
|
||||
", start=" + start + ", end=" + end + "].");
|
||||
board.dump();
|
||||
start = Math.max(start, 0);
|
||||
end = Math.min(end, bwid);
|
||||
}
|
||||
|
||||
// get the smallest drop distance across all of the block columns
|
||||
int dist = board.getHeight() - 1;
|
||||
for (int xpos = start; xpos <= end; xpos++) {
|
||||
dist = Math.min(dist, board.getDropDistance(xpos, yy));
|
||||
}
|
||||
if (dist == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// scoot along the bottom edge of the block, noting the drop
|
||||
// for each column
|
||||
for (int xpos = start; xpos <= end; xpos++) {
|
||||
piece = board.getPiece(xpos, yy);
|
||||
drop(board, piece, xpos, yy, yy + dist, drobs);
|
||||
dropped++;
|
||||
}
|
||||
|
||||
} else {
|
||||
// get the distance to drop the pieces
|
||||
int dist = board.getDropDistance(xx, yy);
|
||||
if (dist == 0) {
|
||||
return 0;
|
||||
}
|
||||
drop(board, piece, xx, yy, yy + dist, drobs);
|
||||
dropped++;
|
||||
}
|
||||
|
||||
return dropped;
|
||||
}
|
||||
|
||||
/** Helpy helper function. */
|
||||
protected final void drop (DropBoard board, int piece,
|
||||
int xx, int yy, int ty, DropObserver drobs)
|
||||
{
|
||||
// don't try to clear things out if we're filling in from off-board
|
||||
if (yy >= 0) {
|
||||
board.setPiece(xx, yy, PIECE_NONE);
|
||||
}
|
||||
board.setPiece(xx, ty, piece);
|
||||
if (drobs != null) {
|
||||
drobs.pieceDropped(piece, xx, yy, xx, ty);
|
||||
}
|
||||
}
|
||||
|
||||
/** Allows puzzle-specific customizations. */
|
||||
protected PieceDropLogic _logic;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// $Id: PuzzleGameDispatcher.java 4145 2006-05-24 01:24:24Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2006 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.server;
|
||||
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.data.InvocationMarshaller;
|
||||
import com.threerings.presents.server.InvocationDispatcher;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
import com.threerings.puzzle.client.PuzzleGameService;
|
||||
import com.threerings.puzzle.data.Board;
|
||||
import com.threerings.puzzle.data.PuzzleGameMarshaller;
|
||||
|
||||
/**
|
||||
* Dispatches requests to the {@link PuzzleGameProvider}.
|
||||
*/
|
||||
public class PuzzleGameDispatcher extends InvocationDispatcher
|
||||
{
|
||||
/**
|
||||
* Creates a dispatcher that may be registered to dispatch invocation
|
||||
* service requests for the specified provider.
|
||||
*/
|
||||
public PuzzleGameDispatcher (PuzzleGameProvider provider)
|
||||
{
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public InvocationMarshaller createMarshaller ()
|
||||
{
|
||||
return new PuzzleGameMarshaller();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void dispatchRequest (
|
||||
ClientObject source, int methodId, Object[] args)
|
||||
throws InvocationException
|
||||
{
|
||||
switch (methodId) {
|
||||
case PuzzleGameMarshaller.UPDATE_PROGRESS:
|
||||
((PuzzleGameProvider)provider).updateProgress(
|
||||
source,
|
||||
((Integer)args[0]).intValue(), (int[])args[1]
|
||||
);
|
||||
return;
|
||||
|
||||
case PuzzleGameMarshaller.UPDATE_PROGRESS_SYNC:
|
||||
((PuzzleGameProvider)provider).updateProgressSync(
|
||||
source,
|
||||
((Integer)args[0]).intValue(), (int[])args[1], (Board[])args[2]
|
||||
);
|
||||
return;
|
||||
|
||||
default:
|
||||
super.dispatchRequest(source, methodId, args);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// $Id: PuzzleGameProvider.java 4145 2006-05-24 01:24:24Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2006 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.server;
|
||||
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
import com.threerings.presents.server.InvocationProvider;
|
||||
import com.threerings.puzzle.client.PuzzleGameService;
|
||||
import com.threerings.puzzle.data.Board;
|
||||
|
||||
/**
|
||||
* Defines the server-side of the {@link PuzzleGameService}.
|
||||
*/
|
||||
public interface PuzzleGameProvider extends InvocationProvider
|
||||
{
|
||||
/**
|
||||
* Handles a {@link PuzzleGameService#updateProgress} request.
|
||||
*/
|
||||
public void updateProgress (ClientObject caller, int arg1, int[] arg2);
|
||||
|
||||
/**
|
||||
* Handles a {@link PuzzleGameService#updateProgressSync} request.
|
||||
*/
|
||||
public void updateProgressSync (ClientObject caller, int arg1, int[] arg2, Board[] arg3);
|
||||
}
|
||||
@@ -0,0 +1,632 @@
|
||||
//
|
||||
// $Id: PuzzleManager.java 4188 2006-06-13 18:03:48Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.server;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.samskivert.util.IntListUtil;
|
||||
import com.samskivert.util.Interval;
|
||||
import com.samskivert.util.RandomUtil;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.dobj.DObject;
|
||||
import com.threerings.presents.dobj.OidList;
|
||||
|
||||
import com.threerings.crowd.data.BodyObject;
|
||||
import com.threerings.crowd.server.CrowdServer;
|
||||
|
||||
import com.threerings.parlor.game.data.GameObject;
|
||||
import com.threerings.parlor.game.server.GameManager;
|
||||
|
||||
import com.threerings.util.MessageBundle;
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.puzzle.Log;
|
||||
import com.threerings.puzzle.data.Board;
|
||||
import com.threerings.puzzle.data.BoardSummary;
|
||||
import com.threerings.puzzle.data.PuzzleCodes;
|
||||
import com.threerings.puzzle.data.PuzzleConfig;
|
||||
import com.threerings.puzzle.data.PuzzleGameMarshaller;
|
||||
import com.threerings.puzzle.data.PuzzleObject;
|
||||
|
||||
/**
|
||||
* Extends the {@link GameManager} with facilities for the puzzle games
|
||||
* that are used in Yohoho. Only features generic to all of our games are
|
||||
* in this base class and additional features are supported both through
|
||||
* the inheritance hierarchy and through delegating helpers (because Java
|
||||
* conveniently doesn't support multiple inheritance).
|
||||
*/
|
||||
public abstract class PuzzleManager extends GameManager
|
||||
implements PuzzleCodes, PuzzleGameProvider
|
||||
{
|
||||
/**
|
||||
* Returns the boards for all players.
|
||||
*/
|
||||
public Board[] getBoards ()
|
||||
{
|
||||
return _boards;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the board summary for the given player index.
|
||||
*/
|
||||
public BoardSummary getBoardSummary (int pidx)
|
||||
{
|
||||
return (_puzobj == null || _puzobj.summaries == null) ? null :
|
||||
_puzobj.summaries[pidx];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this puzzle cares to make use of per-player board
|
||||
* summaries that are sent periodically to all users in the puzzle via
|
||||
* {@link #sendStatusUpdate}. The default implementation returns
|
||||
* <code>false</code>.
|
||||
*/
|
||||
public boolean needsBoardSummaries ()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this puzzle compares board states before it applies
|
||||
* progress events, or after. The default implementation returns
|
||||
* <code>true</code>.
|
||||
*/
|
||||
protected boolean compareBeforeApply ()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the server and client states being out of sync when in
|
||||
* debug mode. The default implementation halts the server.
|
||||
*/
|
||||
protected void handleBoardNotEqual ()
|
||||
{
|
||||
// bail out so that we know something's royally borked
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link BoardSummary#summarize} on the given player's board
|
||||
* summary to refresh the summary information in preparation for
|
||||
* sending along to the client(s).
|
||||
*
|
||||
* @param pidx the player index of the player whose board is to be
|
||||
* summarized.
|
||||
*/
|
||||
public void updateBoardSummary (int pidx)
|
||||
{
|
||||
if (_puzobj.summaries != null && _puzobj.summaries[pidx] != null) {
|
||||
_puzobj.summaries[pidx].summarize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies updateBoardSummary on all the players' boards. AI board
|
||||
* summaries should be updated by the AI logic.
|
||||
*/
|
||||
public void updateBoardSummaries ()
|
||||
{
|
||||
if (_puzobj.summaries != null) {
|
||||
for (int ii = 0; ii < _puzobj.summaries.length; ii++) {
|
||||
if (!isAI(ii) || summarizeAIBoard()) {
|
||||
updateBoardSummary(ii);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void playerGameDidEnd (int pidx)
|
||||
{
|
||||
super.playerGameDidEnd(pidx);
|
||||
|
||||
updateSummaryOnDeath(pidx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the board summary for a player who has been eliminated and
|
||||
* performs an update to communicate this change.
|
||||
*/
|
||||
protected void updateSummaryOnDeath (int pidx)
|
||||
{
|
||||
if (!isAI(pidx)) {
|
||||
// update the board summary with the player's final board
|
||||
updateBoardSummary(pidx);
|
||||
}
|
||||
|
||||
// force a status update
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to have board summaries for AIs automatically generated.
|
||||
*/
|
||||
protected boolean summarizeAIBoard ()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected Class getPlaceObjectClass ()
|
||||
{
|
||||
return PuzzleObject.class;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void didInit ()
|
||||
{
|
||||
super.didInit();
|
||||
|
||||
// save off a casted reference to our puzzle config
|
||||
_puzconfig = (PuzzleConfig)_config;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void didStartup ()
|
||||
{
|
||||
super.didStartup();
|
||||
|
||||
// grab the puzzle object
|
||||
_puzobj = (PuzzleObject)_gameobj;
|
||||
|
||||
// create and fill in our game service object
|
||||
PuzzleGameMarshaller service = (PuzzleGameMarshaller)
|
||||
_invmgr.registerDispatcher(new PuzzleGameDispatcher(this), false);
|
||||
_puzobj.setPuzzleGameService(service);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void gameWillStart ()
|
||||
{
|
||||
int size = getPlayerSlots();
|
||||
if (_boards == null) {
|
||||
// create our arrays
|
||||
_boards = new Board[size];
|
||||
_lastProgress = new long[size];
|
||||
} else {
|
||||
Arrays.fill(_boards, null);
|
||||
}
|
||||
|
||||
// start everyone out with reasonable last progress stamps
|
||||
Arrays.fill(_lastProgress, System.currentTimeMillis());
|
||||
|
||||
// compute the starting difficulty (this has to happen before we
|
||||
// set the seed because that triggers the generation of the boards
|
||||
// on the client)
|
||||
_puzobj.setDifficulty(computeDifficulty());
|
||||
|
||||
// initialize the seed that goes out with this round
|
||||
_puzobj.setSeed(RandomUtil.rand.nextLong());
|
||||
|
||||
// initialize the player boards
|
||||
initBoards();
|
||||
|
||||
// let the game manager start up its business
|
||||
super.gameWillStart();
|
||||
|
||||
// send along an initial status update before we start up the
|
||||
// status update interval
|
||||
sendStatusUpdate();
|
||||
|
||||
long statusInterval = getStatusInterval();
|
||||
if (_statusInterval == null && statusInterval > 0) {
|
||||
// register the status update interval to address subsequent
|
||||
// periodic updates
|
||||
_statusInterval = new Interval(CrowdServer.omgr) {
|
||||
public void expired () {
|
||||
sendStatusUpdate();
|
||||
}
|
||||
};
|
||||
_statusInterval.schedule(statusInterval, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the frequency with which puzzle status updates are
|
||||
* broadcast to the players (which is accomplished via a call to
|
||||
* {@link #sendStatusUpdate} which in turn calls {@link #updateStatus}
|
||||
* wherein derived classes can participate in the status update).
|
||||
* Returning <code>O</code> (the default) indicates that a periodic
|
||||
* status update is not desired.
|
||||
*/
|
||||
protected long getStatusInterval ()
|
||||
{
|
||||
return 0L;
|
||||
}
|
||||
|
||||
/**
|
||||
* When a puzzle game starts, the manager is given the opportunity to
|
||||
* configure the puzzle difficulty based on information known about
|
||||
* the player. Additionally, when the game resets due to the player
|
||||
* clearing the board, etc. this will be called again, so the
|
||||
* difficulty can be ramped up as the player progresses. In situations
|
||||
* where ratings and experience are tracked, the difficulty can be
|
||||
* seeded based on the players prior performance.
|
||||
*/
|
||||
protected int computeDifficulty ()
|
||||
{
|
||||
return DEFAULT_DIFFICULTY;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void gameDidStart ()
|
||||
{
|
||||
super.gameDidStart();
|
||||
|
||||
// log the AI skill levels for games involving AIs as it's useful
|
||||
// when tuning AI algorithms
|
||||
if (_AIs != null) {
|
||||
Log.info("AIs on the job [game=" + _puzobj.which() +
|
||||
", skillz=" + StringUtil.toString(_AIs) + "].");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates (in one puzzle object transaction) all periodically updated
|
||||
* status information.
|
||||
*/
|
||||
protected void sendStatusUpdate ()
|
||||
{
|
||||
_puzobj.startTransaction();
|
||||
try {
|
||||
// Log.info("Updating status [game=" + _puzobj.which() + "].");
|
||||
updateStatus();
|
||||
} finally {
|
||||
_puzobj.commitTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A puzzle periodically (default of once every 5 seconds but
|
||||
* configurable by puzzle) updates status information that is visible
|
||||
* to the user. Derived classes can override this method and effect
|
||||
* their updates by generating events on the puzzle object and they
|
||||
* will be packaged into the update transaction.
|
||||
*/
|
||||
protected void updateStatus ()
|
||||
{
|
||||
// if we're a board summary updating kind of puzzle, do that
|
||||
if (needsBoardSummaries()) {
|
||||
// generate the latest summaries
|
||||
updateBoardSummaries();
|
||||
// then broadcast them to the clients
|
||||
_puzobj.setSummaries(_puzobj.summaries);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a system message with the puzzle bundle.
|
||||
*/
|
||||
protected void systemMessage (String msg)
|
||||
{
|
||||
systemMessage(msg, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a system message with the puzzle bundle.
|
||||
*
|
||||
* @param waitForStart if true, the message will not be sent until the
|
||||
* game has started.
|
||||
*/
|
||||
protected void systemMessage (String msg, boolean waitForStart)
|
||||
{
|
||||
systemMessage(PUZZLE_MESSAGE_BUNDLE, msg, waitForStart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and initializes boards and board summaries (if desired per
|
||||
* {@link #needsBoardSummaries}) for each player.
|
||||
*/
|
||||
protected void initBoards ()
|
||||
{
|
||||
long seed = _puzobj.seed;
|
||||
BoardSummary[] summaries = needsBoardSummaries() ?
|
||||
new BoardSummary[getPlayerSlots()] : null;
|
||||
|
||||
// set up game information for each player
|
||||
for (int ii = 0, nn = getPlayerSlots(); ii < nn; ii++) {
|
||||
boolean needsPlayerBoard = needsPlayerBoard(ii);
|
||||
if (needsPlayerBoard) {
|
||||
// create the game board
|
||||
_boards[ii] = newBoard(ii);
|
||||
_boards[ii].initializeSeed(seed);
|
||||
if (summaries != null) {
|
||||
summaries[ii] = newBoardSummary(_boards[ii]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_puzobj.setSummaries(summaries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this puzzle needs a board for the given player
|
||||
* index. The default implementation only creates boards for occupied
|
||||
* player slots. Derived classes may wish to override this method if
|
||||
* they have specialized board needs, e.g., they need only a single
|
||||
* board for all players.
|
||||
*/
|
||||
protected boolean needsPlayerBoard (int pidx)
|
||||
{
|
||||
return (_puzobj.isOccupiedPlayer(pidx));
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void gameDidEnd ()
|
||||
{
|
||||
if (_statusInterval != null) {
|
||||
// remove the client update interval
|
||||
_statusInterval.cancel();
|
||||
_statusInterval = null;
|
||||
}
|
||||
|
||||
// send along one final status update
|
||||
sendStatusUpdate();
|
||||
|
||||
super.gameDidEnd();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void didShutdown ()
|
||||
{
|
||||
super.didShutdown();
|
||||
|
||||
// make sure our update interval is unregistered
|
||||
if (_statusInterval != null) {
|
||||
// remove the client update interval
|
||||
_statusInterval.cancel();
|
||||
_statusInterval = null;
|
||||
}
|
||||
|
||||
// clear out our service registration
|
||||
_invmgr.clearDispatcher(_puzobj.puzzleGameService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies progress updates received from the client. If puzzle
|
||||
* debugging is enabled, this also compares the client board dumps
|
||||
* provided along with each puzzle event.
|
||||
*/
|
||||
protected void applyProgressEvents (int pidx, int[] gevents, Board[] states)
|
||||
{
|
||||
int size = gevents.length;
|
||||
boolean before = compareBeforeApply();
|
||||
|
||||
for (int ii = 0, pos = 0; ii < size; ii++) {
|
||||
int gevent = gevents[ii];
|
||||
Board cboard = (states == null) ? null : states[ii];
|
||||
|
||||
// if we have state syncing enabled, make sure the board is
|
||||
// correct before applying the event
|
||||
if (before && (cboard != null)) {
|
||||
compareBoards(pidx, cboard, gevent, before);
|
||||
}
|
||||
|
||||
// apply the event to the player's board
|
||||
if (!applyProgressEvent(pidx, gevent, cboard)) {
|
||||
Log.warning("Unknown event [puzzle=" + where() +
|
||||
", pidx=" + pidx + ", event=" + gevent + "].");
|
||||
}
|
||||
|
||||
// maybe we are comparing boards afterwards
|
||||
if (!before && (cboard != null)) {
|
||||
compareBoards(pidx, cboard, gevent, before);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare our server board to the specified sent-back user board.
|
||||
*/
|
||||
protected void compareBoards (int pidx, Board boardstate,
|
||||
int gevent, boolean before)
|
||||
{
|
||||
if (DEBUG_PUZZLE) {
|
||||
Log.info((before ? "About to apply " : "Just applied ") +
|
||||
"[game=" + _puzobj.which() + ", pidx=" + pidx +
|
||||
", event=" + gevent + "].");
|
||||
}
|
||||
if (boardstate == null) {
|
||||
if (DEBUG_PUZZLE) {
|
||||
Log.info("No board state provided. Can't compare.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
boolean equal = _boards[pidx].equals(boardstate);
|
||||
if (!equal) {
|
||||
Log.warning("Client and server board states not equal! " +
|
||||
"[game=" + _puzobj.which() +
|
||||
", type=" + _puzobj.getClass().getName() + "].");
|
||||
}
|
||||
if (DEBUG_PUZZLE) {
|
||||
// if we're debugging, dump the board state every time
|
||||
// we're about to apply an event
|
||||
_boards[pidx].dumpAndCompare(boardstate);
|
||||
}
|
||||
if (!equal) {
|
||||
if (DEBUG_PUZZLE) {
|
||||
handleBoardNotEqual();
|
||||
} else {
|
||||
// dump the board state since we're not debugging and
|
||||
// didn't just do it above
|
||||
_boards[pidx].dumpAndCompare(boardstate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by {@link #updateProgress} to give the server a chance to
|
||||
* apply each game event received from the client to the respective
|
||||
* player's server-side board and, someday, confirm their validity.
|
||||
* Derived classes that make use of the progress updating
|
||||
* functionality should be sure to override this method to perform
|
||||
* their game-specific event application antics. They should first
|
||||
* perform a call to super() to see if the event is handled there.
|
||||
*
|
||||
* @param pidx the player index that submitted the progress event.
|
||||
* @param gevent the progress event itself.
|
||||
* @param cboard a snapshot of the board on the client iff the client has
|
||||
* board syncing enabled (which is only enabled when debugging).
|
||||
*
|
||||
* @return true to indicate that the event was handled.
|
||||
*/
|
||||
protected boolean applyProgressEvent (int pidx, int gevent, Board cboard)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the game manager implementation to mark all active
|
||||
* players as winners. Derived classes may wish to override this
|
||||
* method in order to customize the winning conditions.
|
||||
*/
|
||||
protected void assignWinners (boolean[] winners)
|
||||
{
|
||||
for (int ii = 0; ii < winners.length; ii++) {
|
||||
winners[ii] = _puzobj.isActivePlayer(ii);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns a new starting board for the given player.
|
||||
*/
|
||||
protected abstract Board newBoard (int pidx);
|
||||
|
||||
/**
|
||||
* Creates and returns a new board summary for the given board.
|
||||
* Puzzles that do not make use of board summaries should implement
|
||||
* this method and return <code>null</code>.
|
||||
*/
|
||||
protected abstract BoardSummary newBoardSummary (Board board);
|
||||
|
||||
// documentation inherited from interface PuzzleGameProvider
|
||||
public void updateProgress (ClientObject caller, int roundId, int[] events)
|
||||
{
|
||||
updateProgressSync(caller, roundId, events, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the puzzle manager receives a progress update. It
|
||||
* checks to make sure that the progress update is valid and the
|
||||
* puzzle is still in play and then applies the updates via {@link
|
||||
* #applyProgressEvents}.
|
||||
*/
|
||||
public void updateProgressSync (
|
||||
ClientObject caller, int roundId, int[] events, Board[] states)
|
||||
{
|
||||
// determine the caller's player index in the game
|
||||
int pidx = IntListUtil.indexOf(_playerOids, caller.getOid());
|
||||
if (pidx == -1) {
|
||||
Log.warning("Received progress update for non-player?! " +
|
||||
"[game=" + _puzobj.which() + ", who=" + caller.who() +
|
||||
", ploids=" + StringUtil.toString(_playerOids) + "].");
|
||||
return;
|
||||
}
|
||||
|
||||
// bail if the progress update isn't for the current round
|
||||
if (roundId != _puzobj.roundId) {
|
||||
// only warn if this isn't a straggling update from the
|
||||
// previous round
|
||||
if (roundId != _puzobj.roundId-1) {
|
||||
Log.warning("Received progress update for invalid round, " +
|
||||
"not applying [game=" + _puzobj.which() +
|
||||
", invalidRoundId=" + roundId +
|
||||
", roundId=" + _puzobj.roundId + "].");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// if the game is over, we wing straggling updates
|
||||
if (!_puzobj.isInPlay()) {
|
||||
Log.debug("Ignoring straggling events " +
|
||||
"[game=" + _puzobj.which() +
|
||||
", user=" + getPlayerName(pidx) +
|
||||
", events=" + StringUtil.toString(events) + "].");
|
||||
return;
|
||||
}
|
||||
|
||||
// Log.info("Handling progress events [game=" + _puzobj.which() +
|
||||
// ", pidx=" + pidx + ", roundId=" + roundId +
|
||||
// ", count=" + events.length + "].");
|
||||
|
||||
// note that we received a progress update from this player
|
||||
_lastProgress[pidx] = System.currentTimeMillis();
|
||||
|
||||
// apply the progress events to the player's puzzle state
|
||||
applyProgressEvents(pidx, events, states);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
protected void tick (long tickStamp)
|
||||
{
|
||||
super.tick(tickStamp);
|
||||
|
||||
// every five seconds, we call the inactivity checking code
|
||||
if (_puzobj != null && _puzobj.isInPlay() && checkForInactivity()) {
|
||||
int pcount = getPlayerSlots();
|
||||
for (int ii = 0; ii < pcount && _puzobj.isInPlay(); ii++) {
|
||||
if (!isAI(ii)) {
|
||||
checkPlayerActivity(tickStamp, ii);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether {@link #checkPlayerActivity} should be called
|
||||
* periodically while the game is in play to make sure players are
|
||||
* still active.
|
||||
*/
|
||||
protected boolean checkForInactivity ()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called periodically for each human player to give puzzles a chance
|
||||
* to make sure all such players are engaging in reasonable levels of
|
||||
* activity. The default implementation does naught.
|
||||
*/
|
||||
protected void checkPlayerActivity (long tickStamp, int pidx)
|
||||
{
|
||||
// nothing for now
|
||||
}
|
||||
|
||||
/** A casted reference to our puzzle config object. */
|
||||
protected PuzzleConfig _puzconfig;
|
||||
|
||||
/** A casted reference to our puzzle game object. */
|
||||
protected PuzzleObject _puzobj;
|
||||
|
||||
/** The player boards. */
|
||||
protected Board[] _boards;
|
||||
|
||||
/** The client update interval. */
|
||||
protected Interval _statusInterval;
|
||||
|
||||
/** Used to track the last time we received a progress event from each
|
||||
* player in this puzzle. */
|
||||
protected long[] _lastProgress;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// $Id: PuzzleManagerDelegate.java 3381 2005-03-03 19:36:34Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.server;
|
||||
|
||||
import com.threerings.parlor.game.server.GameManagerDelegate;
|
||||
|
||||
/**
|
||||
* Extends the {@link GameManagerDelegate} mechanism with puzzle manager
|
||||
* specific methods (of which there are currently none).
|
||||
*/
|
||||
public class PuzzleManagerDelegate extends GameManagerDelegate
|
||||
{
|
||||
/**
|
||||
* Constructs a puzzle manager delegate.
|
||||
*/
|
||||
public PuzzleManagerDelegate (PuzzleManager puzmgr)
|
||||
{
|
||||
super(puzmgr);
|
||||
_puzmgr = puzmgr;
|
||||
}
|
||||
|
||||
protected PuzzleManager _puzmgr;
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
//
|
||||
// $Id: PointSet.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.util;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.util.Iterator;
|
||||
|
||||
import com.threerings.puzzle.Log;
|
||||
|
||||
/**
|
||||
* The point set class provides an efficient implementation of a set
|
||||
* containing two-dimensional point values as <code>(x, y)</code>.
|
||||
*/
|
||||
public class PointSet
|
||||
{
|
||||
/**
|
||||
* Creates a point set that can contain points within the given range
|
||||
* of values.
|
||||
*
|
||||
* @param rangeX the maximum x-axis range.
|
||||
* @param rangeY the maximum y-axis range.
|
||||
*/
|
||||
public PointSet (int rangeX, int rangeY)
|
||||
{
|
||||
_rangeX = rangeX;
|
||||
_rangeY = rangeY;
|
||||
_points = new boolean[rangeX][rangeY];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a point to the set and returns whether the point was already
|
||||
* present in the set.
|
||||
*
|
||||
* @param x the point x-coordinate.
|
||||
* @param y the point y-coordinate.
|
||||
*
|
||||
* @return true if the point was already present, false if not.
|
||||
*/
|
||||
public boolean add (int x, int y)
|
||||
{
|
||||
boolean present = _points[x][y];
|
||||
_points[x][y] = true;
|
||||
if (!present) {
|
||||
_count++;
|
||||
}
|
||||
return present;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds all points in the given set to this set.
|
||||
*
|
||||
* @param set the set containing points to add.
|
||||
*/
|
||||
public void addAll (PointSet set)
|
||||
{
|
||||
Iterator iter = set.iterator();
|
||||
Point pt;
|
||||
while ((pt = (Point)iter.next()) != null) {
|
||||
add(pt.x, pt.y);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all points from this set.
|
||||
*/
|
||||
public void clear ()
|
||||
{
|
||||
if (_count == 0) {
|
||||
// no need to clear anything
|
||||
return;
|
||||
}
|
||||
|
||||
for (int xx = 0; xx < _rangeX; xx++) {
|
||||
for (int yy = 0; yy < _rangeY; yy++) {
|
||||
_points[xx][yy] = false;
|
||||
}
|
||||
}
|
||||
_count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this set contains the given point.
|
||||
*
|
||||
* @param x the point x-coordinate.
|
||||
* @param y the point y-coordinate.
|
||||
*
|
||||
* @return true if the set contains the point, false if not.
|
||||
*/
|
||||
public boolean contains (int x, int y)
|
||||
{
|
||||
return (_points[x][y]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this set is empty.
|
||||
*
|
||||
* @return true if the set is empty, false if not.
|
||||
*/
|
||||
public boolean isEmpty ()
|
||||
{
|
||||
return (_count == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator that iterates over the points in this set,
|
||||
* returning them as {@link Point} objects. Note that the iterator
|
||||
* uses a single point object internally, and so callers should create
|
||||
* their own copy of the point if they plan to do something fancy with
|
||||
* it.
|
||||
*
|
||||
* @return the iterator over the set's points.
|
||||
*/
|
||||
public Iterator iterator ()
|
||||
{
|
||||
return new PointIterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given point from the set and returns whether the point
|
||||
* was present in the set.
|
||||
*
|
||||
* @param x the point x-coordinate.
|
||||
* @param y the point y-coordinate.
|
||||
*
|
||||
* @return true if the point was present, false if not.
|
||||
*/
|
||||
public boolean remove (int x, int y)
|
||||
{
|
||||
boolean present = _points[x][y];
|
||||
_points[x][y] = false;
|
||||
if (present) {
|
||||
_count--;
|
||||
}
|
||||
return present;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of points in the set.
|
||||
*
|
||||
* @return the number of points.
|
||||
*/
|
||||
public int size ()
|
||||
{
|
||||
return _count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the point set.
|
||||
*/
|
||||
public String toString ()
|
||||
{
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("[");
|
||||
Iterator iter = iterator();
|
||||
Point val;
|
||||
while ((val = (Point)iter.next()) != null) {
|
||||
buf.append("(").append(val.x);
|
||||
buf.append(",").append(val.y);
|
||||
buf.append(")");
|
||||
|
||||
if (iter.hasNext()) {
|
||||
buf.append(", ");
|
||||
}
|
||||
}
|
||||
return buf.append("]").toString();
|
||||
}
|
||||
|
||||
protected class PointIterator implements Iterator
|
||||
{
|
||||
public boolean hasNext ()
|
||||
{
|
||||
return (_curCount < _count);
|
||||
}
|
||||
|
||||
public Object next ()
|
||||
{
|
||||
if (_curCount == _count) {
|
||||
return null;
|
||||
}
|
||||
|
||||
while (!_points[_curX][_curY]) {
|
||||
advance();
|
||||
}
|
||||
|
||||
_curCount++;
|
||||
_point.setLocation(_curX, _curY);
|
||||
|
||||
if (_curCount < _count) {
|
||||
advance();
|
||||
}
|
||||
|
||||
return _point;
|
||||
}
|
||||
|
||||
public void remove ()
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
protected void advance ()
|
||||
{
|
||||
if ((++_curX) >= _rangeX) {
|
||||
_curX = 0;
|
||||
_curY++;
|
||||
}
|
||||
|
||||
if (_curY >= _rangeY) {
|
||||
Log.warning("Advanced past point range.");
|
||||
_curY = 0;
|
||||
}
|
||||
}
|
||||
|
||||
protected int _curCount = 0;
|
||||
protected int _curX = 0, _curY = 0;
|
||||
protected Point _point = new Point();
|
||||
}
|
||||
|
||||
/** The dimensions of the point array. */
|
||||
protected int _rangeX, _rangeY;
|
||||
|
||||
/** The points in the set. */
|
||||
protected boolean _points[][];
|
||||
|
||||
/** The number of points in the set. */
|
||||
protected int _count;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// $Id: PuzzleContext.java 3524 2005-04-25 21:08:52Z mdb $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.util;
|
||||
|
||||
import com.threerings.util.KeyDispatcher;
|
||||
import com.threerings.util.KeyboardManager;
|
||||
import com.threerings.util.MessageManager;
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.media.FrameManager;
|
||||
|
||||
import com.threerings.parlor.util.ParlorContext;
|
||||
|
||||
/**
|
||||
* Provides access to entities needed by the puzzle services.
|
||||
*/
|
||||
public interface PuzzleContext extends ParlorContext
|
||||
{
|
||||
/**
|
||||
* Returns the username of the local user.
|
||||
*/
|
||||
public Name getUsername ();
|
||||
|
||||
/**
|
||||
* Returns a reference to the message manager used by the client.
|
||||
*/
|
||||
public MessageManager getMessageManager ();
|
||||
|
||||
/**
|
||||
* Provides access to the frame manager.
|
||||
*/
|
||||
public FrameManager getFrameManager ();
|
||||
|
||||
/**
|
||||
* Provides access to the keyboard manager.
|
||||
*/
|
||||
public KeyboardManager getKeyboardManager ();
|
||||
|
||||
/**
|
||||
* Provides access to the key dispatcher.
|
||||
*/
|
||||
public KeyDispatcher getKeyDispatcher ();
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// $Id: PuzzleGameUtil.java 4191 2006-06-13 22:42:20Z ray $
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.puzzle.util;
|
||||
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
import com.threerings.util.KeyTranslatorImpl;
|
||||
import com.threerings.puzzle.client.PuzzleController;
|
||||
import com.threerings.puzzle.client.PuzzlePanel;
|
||||
|
||||
/**
|
||||
* Puzzle game related utilities.
|
||||
*/
|
||||
public class PuzzleGameUtil
|
||||
{
|
||||
/**
|
||||
* Returns a key translator configured with basic puzzle game
|
||||
* mappings.
|
||||
*/
|
||||
public static KeyTranslatorImpl getKeyTranslator ()
|
||||
{
|
||||
KeyTranslatorImpl xlate = new KeyTranslatorImpl();
|
||||
|
||||
if (!PuzzlePanel.isRobotTesting()) {
|
||||
// add the standard pause keys
|
||||
xlate.addPressCommand(
|
||||
KeyEvent.VK_P, PuzzleController.TOGGLE_CHATTING);
|
||||
}
|
||||
|
||||
return xlate;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user