diff --git a/src/java/com/threerings/puzzle/drop/client/DropBoardView.java b/src/java/com/threerings/puzzle/drop/client/DropBoardView.java
index 51a531c2a..dbcca13b8 100644
--- a/src/java/com/threerings/puzzle/drop/client/DropBoardView.java
+++ b/src/java/com/threerings/puzzle/drop/client/DropBoardView.java
@@ -1,5 +1,5 @@
//
-// $Id: DropBoardView.java,v 1.4 2004/08/27 02:20:29 mdb Exp $
+// $Id: DropBoardView.java,v 1.5 2004/08/29 06:50:47 mdb Exp $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
@@ -30,8 +30,13 @@ 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.puzzle.Log;
import com.threerings.puzzle.client.PuzzleBoardView;
import com.threerings.puzzle.client.ScoreAnimation;
import com.threerings.puzzle.data.Board;
@@ -128,34 +133,152 @@ public abstract class DropBoardView extends PuzzleBoardView
}
/**
- * Dirties the rectangle encompassing the piece segment with the given
- * direction and length whose bottom-leftmost corner is at (col,
- * row).
+ * Creates a new piece sprite and places it directly in it's correct
+ * position.
*/
- public void dirtySegment (int dir, int col, int row, int len)
+ public void createPiece (int piece, int sx, int sy)
{
- 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);
+ 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);
}
/**
- * Dirties the rectangle encompassing the specified piece in the
- * board.
+ * Refreshes the piece sprite at the specified location, if no sprite
+ * exists at the location, one will be created. Note: 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 dirtyPiece (int col, int row)
+ public void updatePiece (int sx, int sy)
{
- _remgr.invalidateRegion(_pwid * col, (_phei * row) - _roff,
- _pwid, _phei);
+ updatePiece(_dboard.getPiece(sx, sy), sx, sy);
}
/**
- * Dirties a rectangular region of pieces.
+ * Updates the piece sprite at the specified location, if no sprite
+ * exists at the location, one will be created. Note: 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 dirtyPieces (int xx, int yy, int width, int height)
+ 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));
+ } 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);
+ addSprite(sprite);
+ 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;
+ }
+
+ // otherwise 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);
+ int tpos = ty * _bwid + tx;
+ 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)
{
- _remgr.invalidateRegion(xx*_pwid, yy*_phei, width*_pwid, height*_phei);
}
/**
@@ -192,9 +315,63 @@ public abstract class DropBoardView extends PuzzleBoardView
}
}
+ // 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));
+ if (piece != null) {
+ int ppos = yy * width + xx;
+ getPiecePosition(xx, yy, spos);
+ piece.setLocation(spos.x, spos.y);
+ addSprite(piece);
+ _pieces[ppos] = piece;
+ }
+ }
+ }
+ }
+
+ /**
+ * 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);
}
/**
@@ -206,6 +383,19 @@ public abstract class DropBoardView extends PuzzleBoardView
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 (col,
+ * row).
+ */
+ 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 which makes use of a label sprite
* that is assigned a path that floats it a short distance up the
@@ -294,6 +484,20 @@ public abstract class DropBoardView extends PuzzleBoardView
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)
+ {
+ if (piece == PIECE_NONE) {
+ return null;
+ }
+ ImageSprite sprite = new ImageSprite(getPieceImage(piece));
+ sprite.setRenderOrder(-1);
+ return sprite;
+ }
+
/**
* Populates pos with the most appropriate screen
* coordinates to center a rectangle of the given width and height (in
@@ -371,6 +575,9 @@ public abstract class DropBoardView extends PuzzleBoardView
/** The drop board. */
protected DropBoard _dboard;
+ /** A sprite for every piece displayed in the drop board. */
+ protected Sprite[] _pieces;
+
/** The piece dimensions in pixels. */
protected int _pwid, _phei;
diff --git a/src/java/com/threerings/puzzle/drop/client/DropControllerDelegate.java b/src/java/com/threerings/puzzle/drop/client/DropControllerDelegate.java
index 6100ac019..560845daf 100644
--- a/src/java/com/threerings/puzzle/drop/client/DropControllerDelegate.java
+++ b/src/java/com/threerings/puzzle/drop/client/DropControllerDelegate.java
@@ -1,5 +1,5 @@
//
-// $Id: DropControllerDelegate.java,v 1.5 2004/08/27 02:20:29 mdb Exp $
+// $Id: DropControllerDelegate.java,v 1.6 2004/08/29 06:50:47 mdb Exp $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
@@ -51,7 +51,6 @@ 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.data.DropPieceCodes;
-import com.threerings.puzzle.drop.util.DropPieceProvider;
import com.threerings.puzzle.drop.util.PieceDropLogic;
import com.threerings.puzzle.drop.util.PieceDropper.PieceDropInfo;
import com.threerings.puzzle.drop.util.PieceDropper;
@@ -149,15 +148,6 @@ public abstract class DropControllerDelegate extends PuzzleControllerDelegate
}
}
- /**
- * Get the DropPieceProvider for this puzzle. This is currently
- * only needed if you are using the alwaysfilled property of dropboards.
- */
- protected DropPieceProvider getDropPieceProvider ()
- {
- return null;
- }
-
/**
* Returns the speed with which the next board row should rise into
* place, in pixels per millisecond.
@@ -217,14 +207,14 @@ public abstract class DropControllerDelegate extends PuzzleControllerDelegate
}
// evolve the board to kick-start the game into action
- tryEvolveBoard();
+ unstabilizeBoard();
}
// documentation inherited
protected boolean canClearAction ()
{
-// Log.info("Drop can clear " + _evolving);
- return !_evolving && super.canClearAction();
+// Log.info("Drop can clear " + _stable);
+ return _stable && super.canClearAction();
}
/**
@@ -534,21 +524,21 @@ public abstract class DropControllerDelegate extends PuzzleControllerDelegate
}
// keep dropping the drop block
sprite.drop();
+ return;
+ }
+
+ if (sprite.getDistance() > 0) {
+ sprite.drop();
} else {
- if (sprite.getDistance() > 0) {
- sprite.drop();
+ // remove the sprite
+ _dview.removeSprite(sprite);
- } else {
- // remove the sprite
- _dview.removeSprite(sprite);
+ // apply the pieces to the board
+ applyDropSprite(sprite, col, row);
- // apply the pieces to the board
- applyDropSprite(sprite, col, row);
-
- // perform any new destruction and falling
- tryEvolveBoard();
- }
+ // perform any new destruction and falling
+ unstabilizeBoard();
}
}
@@ -562,62 +552,13 @@ public abstract class DropControllerDelegate extends PuzzleControllerDelegate
// set the pieces in the board
int[] pieces = sprite.getPieces();
_dboard.setSegment(VERTICAL, col, row, pieces);
- // dirty the updated board pieces
- _dview.dirtySegment(VERTICAL, col, row, pieces.length);
- }
-
- /**
- * Calls {@link #tryEvolveBoard(boolean)} with debugging deactivated.
- */
- protected void tryEvolveBoard ()
- {
- tryEvolveBoard(false);
- }
-
- /**
- * Attempts to evolve the board. This involves first calling {@link
- * #canEvolveBoard} and only calling {@link #evolveBoard} if the
- * former returned true. If the board is fully stabilized, {@link
- * #boardDidStabilize} will be called to reinstate the puzzle action.
- */
- protected void tryEvolveBoard (boolean debug)
- {
- // if we can't evolve the board because things are going on, we
- // bail out immediately
- if (!canEvolveBoard()) {
- if (debug) {
- Log.info("Can't evolve board " +
- "[acount=" + _dview.getActionCount() + "].");
- }
- return;
- }
-
- // if we do not evolve the board in any way, let the derived class
- // know that the board stabilized so that they can drop in a new
- // piece if they like or take whatever other action is appropriate
- _evolving = evolveBoard();
- if (debug) {
- Log.info("Evolved board [evolving=" + _evolving + "].");
- }
-
- // if we're no longer evolving and the action has not ended, go
- // ahead and let our derived class know that the board has
- // stabilized so that it can drop in the next piece or somesuch
- if (!_evolving) {
- if (_ctrl.hasAction()) {
- // this will trigger further puzzle activity
- if (debug) {
- Log.info("Board did stabilize");
- }
- boardDidStabilize();
-
- } else {
- if (debug) {
- Log.info("Maybe clearing action.");
- }
- // this will ensure that if we have been postponing action
- // due to board evolution, that it will now be cleared
- maybeClearAction();
+ // create the updated board pieces
+ for (int dy = 0; dy < pieces.length; dy++) {
+ // pieces outside the board are discarded
+ if (row - dy >= 0) {
+ // note: vertical segments are applied counting downwards
+ // from the starting row toward row zero
+ _dview.createPiece(pieces[dy], col, row - dy);
}
}
}
@@ -629,7 +570,7 @@ public abstract class DropControllerDelegate extends PuzzleControllerDelegate
*/
protected boolean canEvolveBoard ()
{
- return (_dview.getActionCount() == 0);
+ return (!_ctrl.isWaiting() && _dview.getActionCount() == 0);
}
/**
@@ -647,6 +588,16 @@ public abstract class DropControllerDelegate extends PuzzleControllerDelegate
*/
protected abstract boolean evolveBoard ();
+ /**
+ * Derived classes should call this method whenever they change some
+ * board state that will require board evolution to restabilize the
+ * board.
+ */
+ protected void unstabilizeBoard ()
+ {
+ _stable = false;
+ }
+
/**
* Called when the board has been fully evolved and is once again
* stable. The default implementation updates the player's local board
@@ -683,7 +634,7 @@ public abstract class DropControllerDelegate extends PuzzleControllerDelegate
*/
protected void animationDidFinish (Animation anim)
{
- tryEvolveBoard();
+ unstabilizeBoard();
}
/**
@@ -766,7 +717,7 @@ public abstract class DropControllerDelegate extends PuzzleControllerDelegate
if (!error) {
// stuff the piece into the board
_dboard.setPiece(col, row, pieces[ii]);
- _dview.dirtyPiece(col, row);
+ _dview.createPiece(pieces[ii], col, row);
}
}
@@ -827,7 +778,7 @@ public abstract class DropControllerDelegate extends PuzzleControllerDelegate
// forcibly land the block if we bounce twice at the same row
if (_bounceStamp == 0 && _bounceRow == bounceRow) {
if (checkBlockLanded("double-bounced", true, true)) {
- tryEvolveBoard();
+ unstabilizeBoard();
}
return;
}
@@ -878,7 +829,7 @@ public abstract class DropControllerDelegate extends PuzzleControllerDelegate
// make sure we weren't cancelled for some reason
if (_bounceStamp != 0) {
if (checkBlockLanded("bounced", true, true)) {
- tryEvolveBoard();
+ unstabilizeBoard();
} else if (_blocksprite != null) {
// take the block sprite out of bouncing mode
@@ -897,31 +848,20 @@ public abstract class DropControllerDelegate extends PuzzleControllerDelegate
*/
protected boolean dropPieces ()
{
- // get a list of the piece columns to be dropped
- List drops = _dropper.getDroppedPieces(_dboard, getDropPieceProvider());
- int size = drops.size();
- if (size == 0) {
- return false;
- }
-
- // drop each column
- for (int ii = 0; ii < size; ii++) {
- PieceDropInfo pdi = (PieceDropInfo)drops.get(ii);
- // Log.info("Dropping column segment [pdi=" + pdi + "].");
-
- // clear the dropping pieces from the board
- _dboard.setSegment(
- VERTICAL, pdi.col, pdi.row, pdi.pieces.length, PIECE_NONE);
-
- // create a piece sprite animating the pieces falling
- DropSprite sprite = _dview.createPieces(
- pdi.col, pdi.row, pdi.pieces, pdi.dist);
- sprite.setVelocity(1.5f * getPieceVelocity(true));
- sprite.addSpriteObserver(_dropMovedHandler);
- _dview.addActionSprite(sprite);
- }
-
- return true;
+ PieceDropper.DropObserver drobs = new PieceDropper.DropObserver() {
+ public void pieceDropped (
+ int piece, int sx, int sy, int dx, int dy) {
+ float vel = getPieceVelocity(true) * 1.5f;
+ long duration = (long)(_dview.getPieceHeight() *
+ Math.abs(dy-sy) / vel);
+ if (sy < 0) {
+ _dview.createPiece(piece, sx, sy, dx, dy, duration);
+ } else {
+ _dview.movePiece(sx, sy, dx, dy, duration);
+ }
+ }
+ };
+ return (_dropper.dropPieces(_dboard, drobs) > 0);
}
/**
@@ -948,6 +888,45 @@ public abstract class DropControllerDelegate extends PuzzleControllerDelegate
((tickStamp - _bounceStamp) >= _bounceInterval)) {
bounceTimerExpired();
}
+
+ // if we can't evolve the board because it doesn't need evolving
+ // or things are going on, we stop here
+ if (_stable || !canEvolveBoard()) {
+ return;
+ }
+
+ // if we do not evolve the board in any way, let the derived class
+ // know that the board stabilized so that they can drop in a new
+ // piece if they like or take whatever other action is appropriate
+ boolean evolving = evolveBoard();
+ boolean debug = false;
+ if (debug) {
+ Log.info("Evolved board [evolving=" + evolving + "].");
+ }
+
+ // if we're no longer evolving and the action has not ended, go
+ // ahead and let our derived class know that the board has
+ // stabilized so that it can drop in the next piece or somesuch
+ if (!evolving) {
+ // no evolving again until someone destabilizes the board
+ _stable = true;
+
+ if (_ctrl.hasAction()) {
+ // this will trigger further puzzle activity
+ if (debug) {
+ Log.info("Board did stabilize");
+ }
+ boardDidStabilize();
+
+ } else {
+ if (debug) {
+ Log.info("Maybe clearing action.");
+ }
+ // this will ensure that if we have been postponing action
+ // due to board evolution, that it will now be cleared
+ maybeClearAction();
+ }
+ }
}
// documentation inherited
@@ -989,7 +968,7 @@ public abstract class DropControllerDelegate extends PuzzleControllerDelegate
// we're un-paused, so we should try evolving the board to start
// things up again
- tryEvolveBoard(true);
+ unstabilizeBoard();
}
/**
@@ -1107,7 +1086,7 @@ public abstract class DropControllerDelegate extends PuzzleControllerDelegate
if (canRise) {
// evolve the board
- tryEvolveBoard();
+ unstabilizeBoard();
} else {
Log.debug("Sticking fork in it [risers=" +
@@ -1170,15 +1149,15 @@ public abstract class DropControllerDelegate extends PuzzleControllerDelegate
/** The drop board. */
protected DropBoard _dboard;
- /** Whether or not we are in the middle of board evolution. */
- protected boolean _evolving;
-
/** Whether the game is using drop block functionality. */
protected boolean _usedrop;
/** Whether the game is using board rising functionality. */
protected boolean _userise;
+ /** Whether or not the board is currently stable. */
+ protected boolean _stable;
+
/** The board dimensions in pieces. */
protected int _bwid, _bhei;
@@ -1241,6 +1220,16 @@ public abstract class DropControllerDelegate extends PuzzleControllerDelegate
}
};
+ /** A piece operation that will update piece sprites as board
+ * positions are updated. */
+ protected DropBoard.PieceOperation _updateBoardOp =
+ new DropBoard.PieceOperation() {
+ public boolean execute (DropBoard board, int col, int row) {
+ _dview.updatePiece(col, row);
+ return true;
+ }
+ };
+
/** The default board row rising velocity. */
protected static final float DEFAULT_RISE_VELOCITY = 100f / 1000f;
diff --git a/src/java/com/threerings/puzzle/drop/data/DropBoard.java b/src/java/com/threerings/puzzle/drop/data/DropBoard.java
index 0a87af285..f01d7af47 100644
--- a/src/java/com/threerings/puzzle/drop/data/DropBoard.java
+++ b/src/java/com/threerings/puzzle/drop/data/DropBoard.java
@@ -1,5 +1,5 @@
//
-// $Id: DropBoard.java,v 1.7 2004/08/27 02:20:30 mdb Exp $
+// $Id: DropBoard.java,v 1.8 2004/08/29 06:50:47 mdb Exp $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
@@ -145,6 +145,15 @@ public class DropBoard extends Board
}
}
+ /**
+ * 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}).
diff --git a/src/java/com/threerings/puzzle/drop/server/DropManagerDelegate.java b/src/java/com/threerings/puzzle/drop/server/DropManagerDelegate.java
index 062fc97e1..22cc5a1fe 100644
--- a/src/java/com/threerings/puzzle/drop/server/DropManagerDelegate.java
+++ b/src/java/com/threerings/puzzle/drop/server/DropManagerDelegate.java
@@ -1,5 +1,5 @@
//
-// $Id: DropManagerDelegate.java,v 1.4 2004/08/27 02:20:30 mdb Exp $
+// $Id: DropManagerDelegate.java,v 1.5 2004/08/29 06:50:47 mdb Exp $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
@@ -129,7 +129,7 @@ public abstract class DropManagerDelegate extends PuzzleManagerDelegate
*/
protected boolean dropPieces (DropBoard board)
{
- return (_dropper.dropPieces(board, null).size() > 0);
+ return (_dropper.dropPieces(board, null) > 0);
}
/**
diff --git a/src/java/com/threerings/puzzle/drop/util/DropPieceProvider.java b/src/java/com/threerings/puzzle/drop/util/DropPieceProvider.java
deleted file mode 100644
index e44c58a7e..000000000
--- a/src/java/com/threerings/puzzle/drop/util/DropPieceProvider.java
+++ /dev/null
@@ -1,41 +0,0 @@
-//
-// $Id: DropPieceProvider.java,v 1.2 2004/08/27 02:20:31 mdb Exp $
-//
-// 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;
-
-/**
- * Does something extraordinary.
- */
-public interface DropPieceProvider
-{
- /**
- * Get the next piece to add to the drop board.
- */
- public int getNextPiece ()
- throws OutOfPiecesException;
-
- /**
- * I lost my touch.
- */
- public static class OutOfPiecesException extends Exception
- {
- }
-}
diff --git a/src/java/com/threerings/puzzle/drop/util/PieceDropper.java b/src/java/com/threerings/puzzle/drop/util/PieceDropper.java
index ee654a08e..08a55a105 100644
--- a/src/java/com/threerings/puzzle/drop/util/PieceDropper.java
+++ b/src/java/com/threerings/puzzle/drop/util/PieceDropper.java
@@ -1,5 +1,5 @@
//
-// $Id: PieceDropper.java,v 1.3 2004/08/27 02:20:31 mdb Exp $
+// $Id: PieceDropper.java,v 1.4 2004/08/29 06:50:47 mdb Exp $
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2004 Three Rings Design, Inc., All Rights Reserved
@@ -22,6 +22,7 @@
package com.threerings.puzzle.drop.util;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import com.samskivert.util.StringUtil;
@@ -71,6 +72,15 @@ public class PieceDropper
}
}
+ /**
+ * 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.
@@ -81,83 +91,63 @@ public class PieceDropper
}
/**
- * Destructively modifies the supplied board to contain pieces dropped
- * in the first of potentially multiple drop positions and returns a
- * list of {@link PieceDropInfo} objects detailing all column segments
- * to be dropped. Note that a single list is used internally to store
- * the drop info objects, and so callers that care to do anything
- * long-term with the drop info should create their own copy of the
- * list or somesuch.
+ * Effects any drops possible on the supplied board (modifying the
+ * board in the progress) and notifying the supplied drop observer of
+ * those drops.
*
- * @param DropPieceProvider if the board should always be filled
- * (as specified by the PieceDropLogic) this will provide
- * information on the newly filled in pieces.
+ * @return the number of pieces dropped.
*/
- public List dropPieces (DropBoard board, DropPieceProvider provider)
+ public int dropPieces (DropBoard board, DropObserver drobs)
{
- int bhei = board.getHeight(), bwid = board.getWidth();
- _drops.clear();
+ int dropped = 0, bhei = board.getHeight(), bwid = board.getWidth();
for (int yy = bhei - 1; yy >= 0; yy--) {
for (int xx = 0; xx < bwid; xx++) {
- // find all drops in this column
- getColumnDrops(board, _drops, xx, yy);
+ 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()) {
- addFillingDrops(board, provider, _drops);
+ 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 _drops;
+ return dropped;
}
/**
- * Analyzes but does not modify the supplied board and returns a list
- * of {@link PieceDropInfo} objects detailing all column segments to
- * be dropped. Note that a single list is used internally to store
- * the drop info objects, and so callers that care to do anything
- * long-term with the drop info should create their own copy of the
- * list or somesuch.
- *
- * @param DropPieceProvider if the board should always be filled
- * (as specified by the PieceDropLogic) this will provide
- * information on the newly filled in pieces.
+ * Computes and effects the drop for the specified piece and any
+ * associated attached pieces. The supplied observer is notified of
+ * all drops.
*/
- public List getDroppedPieces (DropBoard board, DropPieceProvider provider)
- {
- // grab a snapshot of the board within which we're dropping
- // pieces to avoid modifying the source board directly while we're
- // figuring out what to drop where
- if (_board == null) {
- _board = (DropBoard)board.clone();
- } else {
- board.copyInto(_board);
- }
- return dropPieces(_board, provider);
- }
-
- /**
- * Populates the drops list with {@link PieceDropInfo}
- * objects detailing the column segments to be dropped per empty space
- * below. Destructively modifies the given board to reflect the final
- * positions of the column segments once dropped.
- */
- protected void getColumnDrops (DropBoard board, List drops, int x, int y)
+ protected int dropPieces (
+ DropBoard board, int xx, int yy, DropObserver drobs)
{
// skip empty or fixed pieces
- int piece = board.getPiece(x, y);
+ int piece = board.getPiece(xx, yy);
if (!_logic.isDroppablePiece(piece)) {
- return;
+ return 0;
}
+ int dropped = 0;
if (_logic.isConstrainedPiece(piece)) {
// find out where this constrained block starts and ends
- int start = _logic.getConstrainedEdge(board, x, y, LEFT);
- int end = _logic.getConstrainedEdge(board, x, y, RIGHT);
+ 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=" + x + ", y=" + y +
+ "[x=" + xx + ", y=" + yy +
", start=" + start + ", end=" + end + "].");
board.dump();
start = Math.max(start, 0);
@@ -167,182 +157,47 @@ public class PieceDropper
// 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, y));
+ dist = Math.min(dist, board.getDropDistance(xpos, yy));
}
if (dist == 0) {
- return;
+ return 0;
}
- // scoot along the bottom edge of the block dropping each column
+ // scoot along the bottom edge of the block, noting the drop
+ // for each column
for (int xpos = start; xpos <= end; xpos++) {
- addDropInfo(board, drops, true, xpos, y, dist);
+ 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(x, y);
+ int dist = board.getDropDistance(xx, yy);
if (dist == 0) {
- return;
+ return 0;
}
-
- // add the column segment to the list of drops
- addDropInfo(board, drops, false, x, y, dist);
+ drop(board, piece, xx, yy, yy + dist, drobs);
+ dropped++;
}
+
+ return dropped;
}
- /**
- * Adds a {@link PieceDropInfo} object to the drop list detailing
- * dropping of the column segment at the specified location.
- *
- * @param board the working board.
- * @param allowConst whether to allow dropping constrained pieces in
- * the specified column segment.
- * @param x the column x-coordinate.
- * @param y the column segment bottom y-coordinate.
- * @param dist the distance to drop the pieces.
- */
- protected void addDropInfo (DropBoard board, List drops,
- boolean allowConst, int x, int y, int dist)
+ /** Helpy helper function. */
+ protected final void drop (DropBoard board, int piece,
+ int xx, int yy, int ty, DropObserver drobs)
{
- // sanity check our column
- if (x < 0 || x >= board.getWidth()) {
- Log.warning("Requested to add bogus drop info [board=" + board +
- ", drops=" + StringUtil.toString(drops) +
- ", allowConst=" + allowConst +
- ", x=" + x + ", y=" + y + ", dist=" + dist + "].");
- Thread.dumpStack();
+ // don't try to clear things out if we're filling in from off-board
+ if (yy >= 0) {
+ board.setPiece(xx, yy, PIECE_NONE);
}
-
- // traverse up the column looking for an empty or block piece
- // that will terminate this column segment
- int height = getDropHeight(board, allowConst, x, y, dist);
-
- // create the piece drop info object
- PieceDropInfo pdi = new PieceDropInfo(x, y, dist);
-
- // copy in the relevant pieces
- pdi.pieces = new int[height];
- int idx = 0;
- for (int yy = y; yy > (y - height); yy--) {
- pdi.pieces[idx++] = board.getPiece(x, yy);
- }
-
- // update the working copy of the board with the eventual
- // piece locations
- dropPieces(board, pdi);
-
- // add the column segment to the pot
- drops.add(pdi);
- }
-
- /**
- * If we want to keep the board filled at all times,
- * this method will be called to create drop objects for the
- * newly-filled in pieces.
- */
- protected void addFillingDrops (
- DropBoard board, DropPieceProvider provider, List drops)
- {
- boolean out = false;
- // drop in new pieces for empty spaces at the top
- for (int xx=0, bwid=board.getWidth(); xx < bwid; xx++) {
- // get the distance to drop the pieces
- int dist = board.getDropDistance(xx, -1);
- if (dist != 0) {
- PieceDropInfo pdi = new PieceDropInfo(xx, -1, dist);
- pdi.pieces = new int[dist];
- for (int ii=0; ii < dist; ii++) {
- try {
- pdi.pieces[ii] = provider.getNextPiece();
- } catch (DropPieceProvider.OutOfPiecesException oop) {
- // well, how far did we get?
- int[] something = new int[ii];
- System.arraycopy(pdi.pieces, 0, something, 0, ii);
- pdi.pieces = something;
- out = true;
- break;
- }
- }
-
- dropPieces(board, pdi);
- drops.add(pdi);
-
- // if we're outta pieces, bail.
- if (out) {
- return;
- }
- }
+ board.setPiece(xx, ty, piece);
+ if (drobs != null) {
+ drobs.pieceDropped(piece, xx, yy, xx, ty);
}
}
- /**
- * Returns the height of the piece segment to be dropped at the
- * given coordinates.
- */
- protected int getDropHeight (
- DropBoard board, boolean allowConst, int x, int y, int dist)
- {
- int height = 0;
- for (int yy = y; yy >= 0; yy--) {
- int curpiece = board.getPiece(x, yy);
- if (!_logic.isClimbablePiece(allowConst, curpiece, true)) {
- return height;
- }
-
- height++;
-
- if (!_logic.isClimbablePiece(allowConst, curpiece, false)) {
- return height;
- }
- }
-
- return height;
- }
-
- /**
- * Updates the given board to reflect the eventual destination of the
- * given pieces.
- */
- protected void dropPieces (DropBoard board, PieceDropInfo pdi)
- {
- // clear out the original piece positions
- if (!board.setSegment(
- VERTICAL, pdi.col, pdi.row, pdi.pieces.length, PIECE_NONE)) {
- Log.warning("Bogosity encountered when clearing pieces for drop " +
- "[bwid=" + board.getWidth() +
- ", bhei=" + board.getHeight() + ", pdi=" + pdi + "].");
- }
- // place the pieces in their destination positions
- applyPieces(board, pdi);
- }
-
- /**
- * Applies the pieces in the given piece drop info object to the given
- * board at their eventual destination positions.
- */
- protected void applyPieces (DropBoard board, PieceDropInfo pdi)
- {
- int start = pdi.row, end = (pdi.row - pdi.pieces.length);
- int idx = 0;
- boolean error = false;
- for (int yy = (start + pdi.dist); yy > (end + pdi.dist); yy--) {
- error = !board.setPiece(pdi.col, yy, pdi.pieces[idx++]) || error;
- }
- if (error) {
- Log.warning("Bogosity encountered while applying dropped " +
- "pieces to board [bwid=" + board.getWidth() +
- ", bhei=" + board.getHeight() + ", pdi=" + pdi + "].");
- }
- }
-
- /** The piece drop logic used to allow puzzle-specific piece dropping
- * hooks. */
+ /** Allows puzzle-specific customizations. */
protected PieceDropLogic _logic;
-
- /** The list of piece drop info objects detailing the piece drops
- * resulting from the last call to {@link #getDroppedPieces}. */
- protected ArrayList _drops = new ArrayList();
-
- /** The board on which pieces are being dropped. */
- protected DropBoard _board;
}