diff --git a/src/java/com/threerings/parlor/game/data/GameCodes.java b/src/java/com/threerings/parlor/game/data/GameCodes.java index b3b23126d..e137455e9 100644 --- a/src/java/com/threerings/parlor/game/data/GameCodes.java +++ b/src/java/com/threerings/parlor/game/data/GameCodes.java @@ -28,6 +28,13 @@ import com.threerings.presents.data.InvocationCodes; */ public interface GameCodes extends InvocationCodes { + /** The message bundle identifier for general game messages. */ + public static final String GAME_MESSAGE_BUNDLE = "game.general"; + + /** The name of the message event to a placeObject that a player + * was knocked out of a puzzle. */ + public static final String PLAYER_KNOCKED_OUT = "playerKnocked"; + /** The name of the message event to a placeObject that reports * the winners and losers of a game. */ public static final String WINNERS_AND_LOSERS = "winnersAndLosers"; diff --git a/src/java/com/threerings/parlor/game/server/GameManager.java b/src/java/com/threerings/parlor/game/server/GameManager.java index 55b96ecfa..8aca2c6ea 100644 --- a/src/java/com/threerings/parlor/game/server/GameManager.java +++ b/src/java/com/threerings/parlor/game/server/GameManager.java @@ -53,6 +53,8 @@ import com.threerings.parlor.game.data.GameObject; import com.threerings.parlor.game.data.PartyGameConfig; import com.threerings.parlor.server.ParlorSender; +import com.threerings.util.MessageBundle; + /** * The game manager handles the server side management of a game. It * manipulates the game state in accordance with the logic of the game @@ -412,6 +414,26 @@ public class GameManager extends PlaceManager SpeakProvider.sendInfo(_gameobj, msgbundle, msg); } + /** + * Report to the knocked-out player's room that they were knocked out. + */ + protected void reportPlayerKnockedOut (int pidx) + { + BodyObject user = getPlayer(pidx); + if (user == null) { + // body object can be null for ai players + return; + } + + OidList knocky = new OidList(1); + knocky.add(user.getOid()); + + DObject place = CrowdServer.omgr.getObject(user.location); + if (place != null) { + place.postMessage(PLAYER_KNOCKED_OUT, new Object[] { knocky }); + } + } + // documentation inherited protected Class getPlaceObjectClass () { @@ -594,11 +616,22 @@ public class GameManager extends PlaceManager */ protected void handlePartialNoShow () { - // cancel the game - if (_gameobj.state != GameObject.GAME_OVER && - _gameobj.state != GameObject.CANCELLED) { - _gameobj.setState(GameObject.CANCELLED); + // mark the no-show players; this will cause allPlayersReady() to + // think that everyone has arrived, but still allow us to tell who + // has not shown up in gameDidStart() + for (int ii = 0; ii < _playerOids.length; ii++) { + if (_playerOids[ii] == 0) { + _playerOids[ii] = -1; + } } + + // go ahead and start the game; gameDidStart() will take care of + // giving the boot to anyone who isn't around + Log.info("Forcing start of partial no-show game " + + "[game=" + _gameobj.which() + + ", players=" + StringUtil.toString(_gameobj.players) + + ", poids=" + StringUtil.toString(_playerOids) + "]."); + startGame(); } /** @@ -626,6 +659,18 @@ public class GameManager extends PlaceManager // TEMP: clear out our game end tracker _gameEndTracker.clear(); + // TODO: find the right place to put this + // any players who have not claimed that they are ready should now + // be given le boote royale + for (int ii = 0; ii < _playerOids.length; ii++) { + if (_playerOids[ii] == -1) { + Log.info("Booting no-show player [game=" + _gameobj.which() + + ", player=" + getPlayerName(ii) + "]."); + _playerOids[ii] = 0; // unfiddle the blank oid + endPlayerGame(ii); + } + } + // make sure everyone has turned up if (!allPlayersReady()) { Log.warning("Requested to start a game that is still " + @@ -735,6 +780,65 @@ public class GameManager extends PlaceManager applyToDelegates(_tickAIOp); } + /** + * Ends the game for the given player. + */ + public void endPlayerGame (int pidx) + { + // go for a little transactional efficiency + _gameobj.startTransaction(); + try { + // end the player's game + if (_gameobj.playerStatus != null) { + _gameobj.setPlayerStatusAt(GameObject.PLAYER_LEFT_GAME, pidx); + + // let derived classes do some business + playerGameDidEnd(pidx); + } + + String message = MessageBundle.tcompose( + "m.player_game_over", getPlayerName(pidx)); + systemMessage(GAME_MESSAGE_BUNDLE, message); + } finally { + _gameobj.commitTransaction(); + } + + // if it's time to end the game, then do so + if (shouldEndGame()) { + endGame(); + + } else { + // otherwise report that the player was knocked out to other + // people in his/her room + reportPlayerKnockedOut(pidx); + } + } + + /** + * Called when a player has been marked as knocked out but before the + * knock-out status update has been sent to the players. Any status + * information that needs be updated in light of the knocked out + * player can be updated here. + */ + protected void playerGameDidEnd (int pidx) + { + } + + /** + * Called when a player leaves the game in order to determine whether + * the game should be ended based on its current state, which will + * include updated player status for the player in question. The + * default implementation returns true if the game is in play and + * there is only one player left. Derived classes may wish to + * override this method in order to customize the required end-game + * conditions. + */ + protected boolean shouldEndGame () + { + return (_gameobj.isInPlay() && _gameobj.getActivePlayerCount() == 1); + } + + /** * Called when the game is known to be over. This will call some * calldown functions to determine the winner of the game and then diff --git a/src/java/com/threerings/puzzle/server/PuzzleManager.java b/src/java/com/threerings/puzzle/server/PuzzleManager.java index ffb5a5f93..2376327e5 100644 --- a/src/java/com/threerings/puzzle/server/PuzzleManager.java +++ b/src/java/com/threerings/puzzle/server/PuzzleManager.java @@ -124,46 +124,10 @@ public abstract class PuzzleManager extends GameManager updateBoardSummary(pidx); } - // go for a little transactional efficiency - _puzobj.startTransaction(); - try { - // end the player's game - _puzobj.setPlayerStatusAt(GameObject.PLAYER_LEFT_GAME, pidx); + super.endPlayerGame(pidx); - // let derived classes do some business - playerGameDidEnd(pidx); - - // force a status update - updateStatus(); - - // notify the players - String message = MessageBundle.tcompose( - "m.player_game_over", getPlayerName(pidx)); - systemMessage(PUZZLE_MESSAGE_BUNDLE, message); - - } finally { - _puzobj.commitTransaction(); - } - - // if it's time to end the game, then do so - if (shouldEndGame()) { - endGame(); - - } else { - // otherwise report that the player was knocked out to other - // people in his/her room - reportPlayerKnockedOut(pidx); - } - } - - /** - * Called when a player has been marked as knocked out but before the - * knock-out status update has been sent to the players. Any status - * information that needs be updated in light of the knocked out - * player can be updated here. - */ - protected void playerGameDidEnd (int pidx) - { + // force a status update + updateStatus(); } // documentation inherited @@ -195,27 +159,6 @@ public abstract class PuzzleManager extends GameManager _puzobj.setPuzzleGameService(service); } - // documentation inherited - protected void handlePartialNoShow () - { - // mark the no-show players; this will cause allPlayersReady() to - // think that everyone has arrived, but still allow us to tell who - // has not shown up in gameDidStart() - for (int ii = 0; ii < _playerOids.length; ii++) { - if (_playerOids[ii] == 0) { - _playerOids[ii] = -1; - } - } - - // go ahead and start the game; gameDidStart() will take care of - // giving the boot to anyone who isn't around - Log.info("Forcing start of partial no-show game " + - "[game=" + _puzobj.which() + - ", players=" + StringUtil.toString(_puzobj.players) + - ", poids=" + StringUtil.toString(_playerOids) + "]."); - startGame(); - } - // documentation inherited protected void gameWillStart () { @@ -294,17 +237,6 @@ public abstract class PuzzleManager extends GameManager { super.gameDidStart(); - // any players who have not claimed that they are ready should now - // be given le boote royale - for (int ii = 0; ii < _playerOids.length; ii++) { - if (_playerOids[ii] == -1) { - Log.info("Booting no-show player [game=" + _puzobj.which() + - ", player=" + getPlayerName(ii) + "]."); - _playerOids[ii] = 0; // unfiddle the blank oid - endPlayerGame(ii); - } - } - // log the AI skill levels for games involving AIs as it's useful // when tuning AI algorithms if (_AIs != null) { @@ -417,26 +349,6 @@ public abstract class PuzzleManager extends GameManager super.gameDidEnd(); } - /** - * Report to the knocked-out player's room that they were knocked out. - */ - protected void reportPlayerKnockedOut (int pidx) - { - BodyObject user = getPlayer(pidx); - if (user == null) { - // body object can be null for ai players - return; - } - - OidList knocky = new OidList(1); - knocky.add(user.getOid()); - - DObject place = CrowdServer.omgr.getObject(user.location); - if (place != null) { - place.postMessage(PLAYER_KNOCKED_OUT, new Object[] { knocky }); - } - } - // documentation inherited protected void didShutdown () { @@ -559,20 +471,6 @@ public abstract class PuzzleManager extends GameManager } } - /** - * Called when a player leaves the game in order to determine whether - * the game should be ended based on its current state, which will - * include updated player status for the player in question. The - * default implementation returns true if the game is in play and - * there is only one player left. Derived classes may wish to - * override this method in order to customize the required end-game - * conditions. - */ - protected boolean shouldEndGame () - { - return (_puzobj.isInPlay() && _puzobj.getActivePlayerCount() == 1); - } - /** * Overrides the game manager implementation to mark all active * players as winners. Derived classes may wish to override this