bb0d6c5ba4
generics warnings when calling methods with generic types. git-svn-id: svn+ssh://src.earth.threerings.net/vilya/trunk@698 c613c5cb-e716-0410-b11b-feb51c14d237
586 lines
19 KiB
Java
586 lines
19 KiB
Java
//
|
|
// $Id$
|
|
//
|
|
// Vilya library - tools for developing networked games
|
|
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
|
|
// http://www.threerings.net/code/vilya/
|
|
//
|
|
// 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.game.data.GameConfig;
|
|
import com.threerings.parlor.media.ScoreAnimation;
|
|
|
|
import com.threerings.puzzle.client.PuzzleBoardView;
|
|
import com.threerings.puzzle.data.Board;
|
|
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;
|
|
|
|
import static com.threerings.puzzle.Log.log;
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
@Override
|
|
public void init (GameConfig 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 + "].", where);
|
|
}
|
|
_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() {
|
|
@Override
|
|
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 + "].", 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);
|
|
|
|
@Override
|
|
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<Sprite> iter = _actionSprites.iterator(); iter.hasNext(); ) {
|
|
Sprite s = 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
|
|
}
|
|
|
|
@Override
|
|
public void paintBetween (Graphics2D gfx, Rectangle dirtyRect)
|
|
{
|
|
gfx.translate(0, -_roff);
|
|
renderBoard(gfx, dirtyRect);
|
|
renderRisingPieces(gfx, dirtyRect);
|
|
gfx.translate(0, _roff);
|
|
}
|
|
|
|
@Override
|
|
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;
|
|
}
|