Package com.threerings.puzzle.drop.client

Source Code of com.threerings.puzzle.drop.client.DropControllerDelegate

//
// $Id$
//
// Vilya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// http://code.google.com/p/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.Component;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;

import com.samskivert.util.IntListUtil;

import com.threerings.crowd.client.PlaceController;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.crowd.util.CrowdContext;

import com.threerings.media.FrameParticipant;
import com.threerings.media.animation.Animation;
import com.threerings.media.animation.AnimationAdapter;

import com.threerings.puzzle.client.PuzzleController;
import com.threerings.puzzle.client.PuzzleControllerDelegate;
import com.threerings.puzzle.client.PuzzlePanel;
import com.threerings.puzzle.data.Board;
import com.threerings.puzzle.data.BoardSummary;
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.data.DropPieceCodes;
import com.threerings.puzzle.drop.util.PieceDropLogic;
import com.threerings.puzzle.drop.util.PieceDropper;
import com.threerings.puzzle.util.PuzzleContext;

import static com.threerings.puzzle.Log.log;

/**
* Games that wish to make use of the drop puzzle services will need to create an extension of
* this delegate class, customizing it for their particular game and then adding it via
* {@link PlaceController#addDelegate}.
*
* <p> It handles logical actions for a puzzle game that generally consists of 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.
*
* <p> Derived classes must implement {@link #getPieceVelocity} and {@link #evolveBoard}.
*
* <p> Block-dropping puzzles will likely want to override {@link #createNextBlock},
* {@link #blockDidLand}, and {@link #getPieceDropLogic}.
*
* <p> Board-rising puzzles will likely want to override {@link #getRiseVelocity},
* {@link #getRiseDistance}, {@link #getPieceDropLogic}, and {@link #boardDidRise}.
*/
public abstract class DropControllerDelegate extends PuzzleControllerDelegate
    implements DropCodes, DropPieceCodes, FrameParticipant
{
    /** The action command for moving the block to the left. */
    public static final String MOVE_BLOCK_LEFT = "move_block_left";

    /** The action command for moving the block to the right. */
    public static final String MOVE_BLOCK_RIGHT = "move_block_right";

    /** The action command for rotating the block counter-clockwise. */
    public static final String ROTATE_BLOCK_CCW = "rotate_block_ccw";

    /** The action command for rotating the block clockwise. */
    public static final String ROTATE_BLOCK_CW = "rotate_block_cw";

    /** The action command for starting to dropping the block. */
    public static final String START_DROP_BLOCK = "start_drop_block";

    /** The action command for ending dropping the block. */
    public static final String END_DROP_BLOCK = "end_drop_block";

    /** The action command for raising the next rising row. */
    public static final String RAISE_ROW = "raise_row";

    /**
     * Creates a delegate with the specified drop game logic and controller.
     */
    public DropControllerDelegate (PuzzleController ctrl, DropLogic logic)
    {
        super(ctrl);

        // keep this for later
        _ctrl = ctrl;

        // obtain the drop logic parameters
        _usedrop = logic.useBlockDropping();
        _userise = logic.useBoardRising();

        if (_userise) {
            // prepare for board rising
            _risevel = getRiseVelocity();
            _risedist = getRiseDistance();
        }
    }

    @Override
    public void init (CrowdContext ctx, PlaceConfig config)
    {
        super.init(ctx, config);

        // save things off
        PuzzlePanel panel = (PuzzlePanel)_ctrl.getPlaceView();
        _ctx = (PuzzleContext)ctx;
        _dview = (DropBoardView)panel.getBoardView();
        _dpanel = (DropPanel)panel;
        _dboard = (DropBoard)_ctrl.getBoard();

        // obtain the board dimensions
        DropConfig dconfig = (DropConfig)config;
        _bwid = dconfig.getBoardWidth();
        _bhei = dconfig.getBoardHeight();

        // create the piece dropper if appropriate
        PieceDropLogic pdl = getPieceDropLogic();
        if (pdl != null) {
            _dropper = getPieceDropper(pdl);
        }
    }

    /**
     * Returns the speed with which the next board row should rise into place, in pixels per
     * millisecond.
     */
    protected float getRiseVelocity ()
    {
        return DEFAULT_RISE_VELOCITY;
    }

    /**
     * Returns the distance in pixels that each board row will traverse
     * when rising into place.
    */
    protected int getRiseDistance ()
    {
        return DEFAULT_RISE_DISTANCE;
    }

    /**
     * Starts up the action; tries evolving the board to get things going.
     */
    @Override
    protected void startAction ()
    {
        super.startAction();

//         log.info("Starting drop action");

        // add ourselves as a frame participant
        _ctx.getFrameManager().registerFrameParticipant(this);

//        if (_userise) {
//            // make sure the board has its next row of pieces
//            advanceRisingPieces();
//            // we'll set up the risestamp on the first rise tick
//        }

        // if we've a drop block left over from our previous action, set
        // it on its merry way once more
        if (_blocksprite != null) {
            long delta = _dview.getTimeStamp() - _blockStamp;
            log.info("Restarting drop sprite", "delta", delta);
            _blocksprite.fastForward(delta);
            _blockStamp = 0L;
            _dview.addSprite(_blocksprite);

            // if we cleared the action while the drop sprite was bouncing, we need to land the
            // block to get things going again
            if (_blocksprite.isBouncing()) {
                log.info("Ended on a bounce, landing the block and starting things up.");
                checkBlockLanded("bounced", true, true);
            }
        }

        // evolve the board to kick-start the game into action
        unstabilizeBoard();
    }

    @Override
    protected boolean canClearAction ()
    {
        if (!_stable) {
            log.info("Rejecting canClear() request because not stable.");
        }
        return _stable && super.canClearAction();
    }

    /**
     * Clears out all of the action in the board; removes any drop block sprites, any pieces
     * rising in the board, and resets the animation timestamps.
     */
    @Override
    protected void clearAction ()
    {
        super.clearAction();

//         log.info("Clearing drop action.");

        // do away with the bounce interval
        _bounceStamp = 0;
        _bounceRow = Integer.MIN_VALUE;

        // kill any active drop block
        if (_blocksprite != null) {
            _dview.removeSprite(_blocksprite);
            _blockStamp = _dview.getTimeStamp();
        }

        // reset intermediate rising timestamps
        _rpstamp = 0;
        _zipstamp = 0;
        _fastDrop = false;

        // remove ourselves as a frame participant
        _ctx.getFrameManager().removeFrameParticipant(this);
    }

    @Override
    public void gameDidEnd ()
    {
        super.gameDidEnd();

        // clear out the drop block sprite
        _blocksprite = null;

        // reset ourselves back to pre-game conditions
        _risestamp = 0;
//        _dview.setRisingPieces(null);
    }

    @Override
    public boolean handleAction (ActionEvent action)
    {
        // handle any block-related movement actions
        if (handleBlockAction(action)) {
            return true;
        }

        String cmd = action.getActionCommand();
        if (cmd.equals(START_DROP_BLOCK)) {
            handleDropBlock(true);

        } else if (cmd.equals(END_DROP_BLOCK)) {
            handleDropBlock(false);

        } else {
            return super.handleAction(action);
         }

        return true;
    }

    @Override
    public void setBoard (Board board)
    {
        super.setBoard(board);

        // update the casted board reference
        _dboard = (DropBoard)board;
        // and update our self-summary
        updateSelfSummary();
    }

    protected boolean handleBlockAction (ActionEvent action)
    {
        String cmd = action.getActionCommand();
        boolean handled = false;

        if (cmd.equals(MOVE_BLOCK_LEFT)) {
            handleMoveBlock(LEFT);
            handled = true;

        } else if (cmd.equals(MOVE_BLOCK_RIGHT)) {
            handleMoveBlock(RIGHT);
            handled = true;

        } else if (cmd.equals(ROTATE_BLOCK_CCW)) {
            handleRotateBlock(CCW);
            handled = true;

        } else if (cmd.equals(ROTATE_BLOCK_CW)) {
            handleRotateBlock(CW);
            handled = true;
        }

        if (handled && _blocksprite != null) {
            // land the block if it's been placed on something solid as a result of one of the
            // above actions
            String source = "fiddled [cmd=" + cmd + "]";
            if (checkBlockLanded(source, false, false)) {
                startBounceTimer(source);
            }
        }

        return handled;
    }

    /**
     * Handles block moved events.
     */
    protected void handleMoveBlock (int dir)
    {
        if (_blocksprite == null) {
            return;
        }

        // gather information regarding the attempted move
        Rectangle bb = _blocksprite.getBoardBounds();
        int row = _blocksprite.getRow(), col = _blocksprite.getColumn();
        int dx = (dir == LEFT) ? -1 : 1;

        // if the sprite has made it to the bottom of the board then we don't want to allow it to
        // "virtually" fall any further because of the bounce interval
        float pctdone = (row >= (_bhei - 1)) ? 0 :
             _blocksprite.getPercentDone(_dview.getTimeStamp());

        // get the drop block position resulting from the move
        Point pos = _dboard.getForgivingMove(bb.x, bb.y, bb.width, bb.height, dx, 0, pctdone);
        if (pos != null) {
            int frow = row + (pos.y - bb.y);
            int fcol = col + (pos.x - bb.x);
            // log.info("Valid move", "row", frow, "col", col);
            _blocksprite.setBoardLocation(frow, fcol);
        }
    }

    /**
     * Handles block rotation events.
     */
    protected void handleRotateBlock (int dir)
    {
        if (_blocksprite == null) {
            return;
        }

        // gather information regarding the attempted rotation
        int[] rows = _blocksprite.getRows();
        int[] cols = _blocksprite.getColumns();
        // if the sprite has made it to the bottom of the board then we don't want to allow it to
        // "virtually" fall any further because of the bounce interval
        float pctdone = (rows[0] >= (_bhei - 1)) ? 0 :
            _blocksprite.getPercentDone(_dview.getTimeStamp());

        // get the drop block position resulting from the rotation
        int[] info = _dboard.getForgivingRotation(
            rows, cols, _blocksprite.getOrientation(), dir,
            getRotationType(), pctdone, _blocksprite.canPopup());
        if (info != null) {
//             log.info("Found valid rotation",
//                 "orient", DirectionUtil.toShortString(info[0]), "col", info[1], "row", info[2],
//                 "blocksprite", _blocksprite);

            // update the piece image
            _dview.rotateDropBlock(_blocksprite, info[0]);

            // place the block in its newly rotated location
            _blocksprite.setBoardLocation(info[2], info[1]);

            if (info[3] != 0) {
                _blocksprite.didPopup();
            }

            // let derived classes do what they will
            blockDidRotate(dir);
        }
    }

    /**
     * Called when the drop block has rotated in the specified direction to allow derived classes
     * to engage in any game-specific antics.
     */
    protected void blockDidRotate (int dir)
    {
    }

    /**
     * Returns the rotation type used by this drop game. Either {@link DropBoard#RADIAL_ROTATION}
     * or {@link DropBoard#INPLACE_ROTATION}.
     */
    protected int getRotationType ()
    {
        return DropBoard.RADIAL_ROTATION;
    }

    /**
     * Handles drop block events.
     */
    protected void handleDropBlock (boolean fast)
    {
        _fastDrop = fast;

        // only allow changing the piece velocity if we're not bouncing
        if (_blocksprite != null && _bounceStamp == 0) {
            // log.info("Updating drop block velocity", "fast", fast);
            _blocksprite.setVelocity(getPieceVelocity(fast));
        }
    }

    /**
     * Returns the drop sprite velocity to assign to a new drop sprite.
     */
    protected abstract float getPieceVelocity (boolean fast);

    /**
     * Handles creation and dropping of the next dropping block.
     */
    protected void dropNextBlock ()
    {
        if (_blocksprite != null || !_ctrl.hasAction()) {
            log.info("Not dropping block", "bs", _blocksprite != null, "action", _ctrl.hasAction());
            return;
        }

        // determine whether or not the game should be ended because we can't drop the next block
        if (checkDropEndsGame()) {
            return;
        }

        int pidx = _ctrl.getPlayerIndex();
        if (pidx != -1 && !_ctrl.isGameOver() && _puzobj.isActivePlayer(pidx)) {
            // create the next block
            _blocksprite = createNextBlock();
            if (_blocksprite != null) {
                // reset the drop block fast-drop state
                _fastDrop = false;

                // configure and add the drop block sprite
                _blocksprite.setVelocity(getPieceVelocity(_fastDrop));
                _blocksprite.addSpriteObserver(_dropMovedHandler);
                _dview.addSprite(_blocksprite);

                // update the next block display
                _dpanel.setNextBlock(peekNextPieces());

                // make sure the block has somewhere to go
                if (checkBlockLanded("next-block", false, true)) {
                    startBounceTimer("next-block");
                }
            }

            // clear out our last bounce row
            _bounceRow = Integer.MIN_VALUE;
        }
    }

    /**
     * Called by {@link #dropNextBlock} to determine whether the game should be ended rather than
     * dropping the next block because the board is filled and a new block cannot enter. If true
     * is returned, the drop controller assumes that the derived class will have ended or reset
     * the game as appropriate and will simply abandon its attempt to drop the next block.
     */
    protected boolean checkDropEndsGame ()
    {
        return false;
    }

    /**
     * Called only for block-dropping puzzles when it's time to create the next drop block.
     * Returns the drop block sprite if it was successfully created, or <code>null</code> if it
     * was not.
     */
    protected DropBlockSprite createNextBlock ()
    {
        // nothing for now
        return null;
    }

    /**
     * Take a peek at the next pieces.
     */
    protected int[] peekNextPieces ()
    {
        return null;
    }

    /**
     * Called when a drop sprite posts a piece moved event.
     */
    protected void handleDropSpriteMoved (DropSprite sprite, long when, int col, int row)
    {
        if (sprite instanceof DropBlockSprite) {
            if (checkBlockLanded("piece-moved", false, true)) {
                startBounceTimer("piece-moved");
            }
            // keep dropping the drop block
            sprite.drop();
            return;
        }

        if (sprite.getDistance() > 0) {
            sprite.drop();

        } else {
            // remove the sprite
            _dview.removeSprite(sprite);

            // apply the pieces to the board
            applyDropSprite(sprite, col, row);

            // perform any new destruction and falling
            unstabilizeBoard();
        }
    }

    /**
     * Applies the pieces in the given sprite to the specified column and row in the board. Called
     * when a drop sprite has finished traversing its entire distance.
     */
    protected void applyDropSprite (DropSprite sprite, int col, int row)
    {
        // set the pieces in the board
        int[] pieces = sprite.getPieces();
        _dboard.setSegment(VERTICAL, col, row, pieces);
        // 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);
            }
        }
    }

    /**
     * Called to determine whether it is safe to evolve the board. The default implementation does
     * not allow board evolution if there are sprites or animations active on the board.
     */
    protected boolean canEvolveBoard ()
    {
        return (_dview.getActionCount() == 0);
    }

    /**
     * Evolves the board to an unchanging state. If the board is in a state where pieces should
     * react with one another to cause changes to the board state (such as piece dropping via
     * {@link #dropPieces}, piece destruction, and/or piece joining), this is where that process
     * should be effected.
     *
     * <p> When no further evolution is possible and the board has stabilized this method should
     * return false to indicate that such action should be taken. That will result in a follow-up
     * call to {@link #boardDidStabilize} (assuming that the action was not cleared prior to the
     * final stabilization of the board).
     */
    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 summary and drops the next block into the
     * board, but derived classes may wish to perform custom actions if they don't use drop blocks
     * or have other requirements.
     */
    protected void boardDidStabilize ()
    {
        updateSelfSummary();
        dropNextBlock();
    }

    /**
     * Updates the player's own local board summary to reflect the local copy of the player's
     * board which is likely to be more up to date than the server-side board from which all other
     * player board summaries originate.
     */
    public void updateSelfSummary ()
    {
        int pidx = _ctrl.getPlayerIndex();
        if (pidx != -1 && _puzobj != null && _puzobj.summaries != null) {
            BoardSummary bsum = _puzobj.summaries[pidx];
            bsum.setBoard(_dboard);
            bsum.summarize();
            _dpanel.setSummary(pidx, bsum);
        }
    }

    /**
     * Called when an animation finishes doing its business. Derived classes may wish to override
     * this method but should be sure to call <code>super.animationDidFinish()</code>.
     */
    protected void animationDidFinish (Animation anim)
    {
        unstabilizeBoard();
    }

    /**
     * Checks whether the drop block can continue dropping and lands its pieces if not. Returns
     * whether at least one piece of the block has landed; note that the other piece may need
     * subsequent dropping.
     *
     * @param commit if true, the block landing is committed, if false, it is only checked, not
     * committed.
     * @param atTop whether the block sprite is to be treated as being at the top of its current
     * row.
     */
    protected boolean checkBlockLanded (String source, boolean commit, boolean atTop)
    {
        if (_blocksprite == null) {
            return true;
        }

        // check to see that both pieces can continue dropping
        int[] rows = _blocksprite.getRows();
        int[] cols = _blocksprite.getColumns();

        // TODO: we may need to limit pctdone here to account for landing
        // on the bottom of the board.
        float pctdone = (atTop) ? 0.0f :
            _blocksprite.getPercentDone(_dview.getTimeStamp());

//        log.info("Checking landed",
//            "source", source, "bounceRow", _bounceRow, "rows", rows, "cols", cols,
//            "orient", DirectionUtil.toShortString(_blocksprite.getOrientation()),
//            "commit", commit, "pctdone", pctdone);

        if (_dboard.isValidDrop(rows, cols, pctdone)) {
            if (commit) {
                log.info("Not valid drop",
                    "source", source, "commit", commit, "atTop", atTop, "pctdone", pctdone);
            }
            return false;
        }

        // if we're committing the landing, remove the sprite and update the board and all that
        if (commit) {
            // give sub-classes a chance to do any pre-landing business
            blockWillLand();

            // stamp the pieces into the board
            int[] pieces = _blocksprite.getPieces();
            boolean error = false;
            for (int ii = 0; ii < pieces.length; ii++) {
                if (rows[ii] >= 0) {
                    int col = cols[ii], row = rows[ii];

                    // sanity-check the block to make sure it's located in a valid position, and
                    // that we aren't somehow overwriting an existing piece
                    if (col < 0 || col >= _bwid || row >= _bhei) {
                        log.warning("Placing drop block piece outside board bounds!?",
                            "x", col, "y", row, "pidx", ii, "blocksprite", _blocksprite);
                        error = true;

                    } else {
                        int cpiece = _dboard.getPiece(col, row);
                        if (cpiece != PIECE_NONE) {
                            log.warning("Placing drop block piece onto occupied board position!?",
                                "x", col, "y", row, "pidx", ii, "blocksprite", _blocksprite);
                            error = true;
                        }
                    }

                    if (!error) {
                        // stuff the piece into the board
                        _dboard.setPiece(col, row, pieces[ii]);
                        _dview.createPiece(pieces[ii], col, row);
                    }
                }

                if (DEBUG_PUZZLE && error) {
                    _dboard.dump();
                    log.warning("Bailing out in a flaming pyre of glory.");
                    System.exit(0);
                }
            }

            // remove the drop block sprite
            _dview.removeSprite(_blocksprite);
            _blocksprite = null;

            // give sub-classes a chance to do any post-landing business
            blockDidLand();
        }

        return true;
    }

    /**
     * Called only for block-dropping puzzles when the drop block is about to land on something.
     * Derived classes may wish to override this method to perform game-specific actions such as
     * queueing up a "block placed" progress event.
     */
    protected void blockWillLand ()
    {
        // nothing for now
    }

    /**
     * Called only for block-dropping puzzles when the drop block lands on something. Derived
     * classes may wish to override this method to perform any game-specific actions.
     */
    protected void blockDidLand ()
    {
        // nothing for now
    }

    /**
     * Called when a block lands. We give the user a smidgen of time to continue to fiddle with
     * the block before we actually land it. If the block is still landed when the bounce timer
     * expires, we commit the landing, otherwise we let the block keep falling.
     */
    protected void startBounceTimer (String source)
    {
        int bounceRow = IntListUtil.getMaxValue(_blocksprite.getRows());
//         log.info("startBounceTimer",
//             "source", source, "bounceStamp", _bounceStamp, "time", _dview.getTimeStamp(),
//             "bounceRow", _bounceRow, "nbounceRow", bounceRow);

        // forcibly land the block if we bounce twice at the same row
        if (_bounceStamp == 0 && _bounceRow == bounceRow) {
            if (checkBlockLanded("double-bounced", true, true)) {
                unstabilizeBoard();
            }
            return;
        }

        // if the bounce "timer" is already started, the user probably did something like rotate
        // the piece while it was bouncing (which is why we give them the bounce interval), so we
        // don't reset
        if (_bounceStamp == 0) {
            // slow the piece down so that it doesn't fly past the coordinates at which it's
            // potentially landing; we have to do this before we tell the sprite that it's bouncing
            // because changing the velocity fiddles with the rowstamp and we're going to reset the
            // rowstamp when we tell the sprite that it's bouncing
            _blocksprite.setVelocity(getPieceVelocity(false));

            // set up our bounce interval (it depends on the current piece velocity and so must be
            // set at the time we bounce)
            _bounceInterval = (int)
                ((_dview.getPieceHeight() * BOUNCE_FRACTION) /
                 getPieceVelocity(false));
//             log.info("bounce", "bounceInterval", _bounceInterval,
//                 "phei", _dview.getPieceHeight(), "vel", getPieceVelocity(false));

            // make a note of the time we started bouncing
            _bounceStamp = _dview.getTimeStamp();

            // and the row at which we're bouncing
            _bounceRow = bounceRow;

            // put the block sprite into bouncing mode
            _blocksprite.setBouncing(true);
        }
    }

    /**
     * Called when the bounce timer expires. Herein we either commit the landing of a block if it
     * is still landed or let it keep falling if it is no longer landed.
     */
    protected void bounceTimerExpired ()
    {
//         log.info("bounceTimerExpired",
//             "bounceStamp", _bounceStamp, "time", _dview.getTimeStamp(), "bounceRow", _bounceRow);

        // make sure we weren't cancelled for some reason
        if (_bounceStamp != 0) {
            if (checkBlockLanded("bounced", true, true)) {
                unstabilizeBoard();

            } else if (_blocksprite != null) {
                // take the block sprite out of bouncing mode
                _blocksprite.setBouncing(false);
            }
            _bounceStamp = 0;
        }
    }

    /**
     * Drops any pieces that need dropping and returns whether any pieces were dropped. Derived
     * classes that would like to drop their pieces should include a call to this method in their
     * {@link #evolveBoard} implementation, and must also override {@link #getPieceDropLogic} to
     * provide their game-specific piece dropper implementation.
     */
    protected boolean dropPieces ()
    {
        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);
    }

    /**
     * Returns the piece drop logic used to drop any pieces that need dropping in the board.
     * Derived classes that intend to make use of {@link #dropPieces} must implement this method
     * and return a reference to their game-specific piece dropper implementation.
     */
    protected PieceDropLogic getPieceDropLogic ()
    {
        return null;
    }

    /**
     * Returns the piece dropper used to drop any pieces that need dropping in the board.
     */
    protected PieceDropper getPieceDropper (PieceDropLogic logic)
    {
        return new PieceDropper(logic);
    }

    // documentation inherited
    public void tick (long tickStamp)
    {
//        if (_userise && (tickStamp >= _risesent + RISE_INTERVAL)) {
//            _risesent += RISE_INTERVAL;
//            raiseBoard(tickStamp);
//        }

        // check the bounce timer
        if ((_bounceStamp != 0) &&
            ((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;

            // this will trigger further puzzle activity
            if (debug) {
                log.info("Board did stabilize");
            }
            boardDidStabilize();

            // ensure that if we have been postponing action due to board evolution, that it will
            // now be cleared
            if (!_ctrl.hasAction()) {
                if (debug) {
                    log.info("Maybe clearing action.");
                }
                maybeClearAction();
            }
        }
    }

    // documentation inherited
    public Component getComponent ()
    {
        return null;
    }

    // documentation inherited
    public boolean needsPaint ()
    {
        return false;
    }

    /**
     * Sets whether the board rising is paused.
     */
    public void setRisingPaused (boolean paused)
    {
        if (paused && _rpstamp == 0) {
            // pause the board
            _rpstamp = _dview.getTimeStamp();

        } else if (!paused && _rpstamp != 0) {
            // un-pause the board
            long delta = _dview.getTimeStamp() - _rpstamp;
            _risestamp += delta;
            if (_zipstamp != 0) {
                _zipstamp += delta;
            }
            _rpstamp = 0;
        }
    }

    /**
     * Causes the board to zip quickly to the next row.
     */
    public void zipToNextRow ()
    {
        // don't overwrite an existing zip
        if (_zipstamp == 0) {
            // if we're paused, inherit the pause time, otherwise use the current time
            if (_rpstamp != 0) {
                _zipstamp = _rpstamp;
            } else {
                _zipstamp = _dview.getTimeStamp();
            }
        }
    }

    /**
     * Called periodically on the frame tick. Raises the board row based on the time since the
     * current row traversal began.
     */
    /*
    protected void raiseBoard (long tickStamp)
    {
        // don't raise if rising is paused or the action is cleared
        if (_rpstamp != 0 || !_ctrl.hasAction()) {
            return;
        }

        // initialize the rise stamp the first time we're risen
        if (_risestamp == 0) {
            _risestamp = tickStamp;
            _risesent = _risestamp;
        }

        // determine how far we've risen
        long msecs = tickStamp - _risestamp;
        float travpix = msecs * _risevel;

        // account for any zipping effect
        long zipsecs = 0;
        if (_zipstamp > 0) {
            zipsecs = tickStamp - _zipstamp;
            // make sure we don't zip past the top
            float zippix = (zipsecs * _risevel * 15);
            if (travpix < _risedist) {
                travpix += zippix;
                travpix = Math.min(travpix, _risedist);
            }
        }

        float pctdone = travpix / _risedist;

        boolean rose = false;
        if (pctdone >= 1.0f) {
            rose = true;
            if (_zipstamp > 0) {
                // clear out any zip stamp
                _zipstamp = 0;
                _risestamp = tickStamp;
                pctdone = 1f;

            } else {
                long used = (long)(_risedist / _risevel);
                _risestamp += used;
            }

            // give sub-classes a chance to do their thing
            boardWillRise();
        }

        // update the board display
        int ypos = ((int)(_risedist * pctdone)) % _risedist;
        _dview.setRiseOffset(ypos);

        if (rose) {
            // check to see if this means doom and defeat (even though the game might be over, we
            // still want to advance the piece packet one last time and do the last rise so that
            // the server can tell that we kicked the proverbial bucket)
            boolean canRise = checkCanRise();

            // apply the rising row pieces to the board
            int[] pieces = _dview.getRisingPieces();
            _dboard.applyRisingPieces(pieces);

            // set up the next row of rising pieces
            _dview.setRisingPieces(null);
            advanceRisingPieces();

            // give sub-classes a chance to do their thing
            boardDidRise();

            if (canRise) {
                // evolve the board
                unstabilizeBoard();

            } else {
                log.debug("Sticking fork in it", "risers",  pieces);

                // let the controller know that we're done for
                _ctrl.resetGame();
            }
        }

//         log.info("Board rise",
//             "msecs", msecs, "roff", ypos, "pctdone", pctdone, "zipsecs", zipsecs);
    }
    */

    /**
     * Called to determine whether or not rising a new row into the board is legal. The default
     * implementation will return false if the top row of the board contains any pieces.
     */
    protected boolean checkCanRise ()
    {
        return !_dboard.rowContainsPieces(0, PIECE_NONE);
    }

    /**
     * Called only for board-rising puzzles before effecting the rising of the board by one row.
     * Derived classes may wish to override this method to add any desired behaviour, but should
     * be sure to call <code>super.boardWillRise()</code>.
     */
    protected void boardWillRise ()
    {
        // nothing for now
    }

    /**
     * Called only for board-rising puzzles when the board has finished rising one row. Derived
     * classes may wish to override this method to add any desired behaviour, but should be sure
     * to call <code>super.boardDidRise()</code>.
     */
    protected void boardDidRise ()
    {
        // nothing for now
    }

    /** The puzzle context. */
    protected PuzzleContext _ctx;

    /** Our puzzle controller. */
    protected PuzzleController _ctrl;

    /** The drop panel. */
    protected DropPanel _dpanel;

    /** The drop board view. */
    protected DropBoardView _dview;

    /** The drop board. */
    protected DropBoard _dboard;

    /** 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;

    /** The distance the board row travels in pixels. */
    protected int _risedist;

    /** The speed with which the board rises in pixels per millisecond. */
    protected float _risevel;

    /** The drop block sprite associated with the landing block, if any. */
    protected DropBlockSprite _blocksprite;

    /** The piece dropper used to drop pieces in the board if the puzzle chooses to make use of
     * piece dropping functionality. */
    protected PieceDropper _dropper;

    /** The time at which the board rise was paused. */
    protected long _rpstamp;

    /** The time at which the last board rise began. */
    protected long _risestamp;

    /** The time at which we last fired off a board rising event. */
    protected long _risesent;

    /** The time at which we were requested to start zipping. */
    protected long _zipstamp;

    /** The duration of the bounce interval. */
    protected int _bounceInterval;

    /** The time at which we last started bouncing, or 0. */
    protected long _bounceStamp;

    /** The row at which we last bounced, or {@link Integer#MIN_VALUE}. */
    protected int _bounceRow;

    /** The timestamp used to keep track of when the drop block was removed so that we can
     * fast-forward it when restored. */
    protected long _blockStamp;

    /** Whether the drop blocks are currently dropping quickly. */
    protected boolean _fastDrop;

    /** Used to evolve the board following the completion of animations. */
    protected AnimationAdapter _evolveObserver = new AnimationAdapter() {
        @Override
        public void animationCompleted (Animation anim, long when) {
            animationDidFinish(anim);
        }
    };

    /** Used to listen to drop sprites and react to their move events. */
    protected DropSpriteObserver _dropMovedHandler = new DropSpriteObserver() {
        public void pieceMoved (
            DropSprite sprite, long when, int col, int row) {
            handleDropSpriteMoved(sprite, when, col, row);
        }
    };

    /** 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;

    /** The default board row rising distance. */
    protected static final int DEFAULT_RISE_DISTANCE = 20;

    /** The delay in milliseconds between board rising intervals. */
    protected static final long RISE_INTERVAL = 50L;

    /** Defines the distance of a piece that we allow to bounce before we land it. */
    protected static final float BOUNCE_FRACTION = 0.125f;
}
TOP

Related Classes of com.threerings.puzzle.drop.client.DropControllerDelegate

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.