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,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;
|
||||
}
|
||||
Reference in New Issue
Block a user