use of soc.message.SOCFirstPlayer in project JSettlers2 by jdmonin.
the class SOCGameHandler method forceEndGameTurn.
/**
* Try to force-end the current player's turn in this game.
* Alter game state and send messages to players.
* Will call {@link #endGameTurn(SOCGame, SOCPlayer, boolean)} if appropriate.
* Will send gameState and current player (turn) to clients.
*<P>
* If the current player has lost connection, send the {@link SOCLeaveGame LEAVEGAME}
* message out <b>before</b> calling this method.
*<P>
* Assumes, as {@link #endGameTurn(SOCGame, SOCPlayer, boolean)} does:
* <UL>
* <LI> ga.canEndTurn already called, returned false
* <LI> ga.takeMonitor already called (not the same as {@link SOCGameList#takeMonitorForGame(String)})
* <LI> gamelist.takeMonitorForGame is NOT called, we do NOT have that monitor
* </UL>
* @param ga Game to force end turn
* @param plName Current player's name. Needed because if they have been disconnected by
* {@link #leaveGame(SOCGame, Connection)},
* their name within game object is already null.
* @return true if the turn was ended and game is still active;
* false if we find that all players have left and
* the gamestate has been changed here to {@link SOCGame#OVER}.
*
* @see #endGameTurnOrForce(SOCGame, int, String, Connection, boolean)
* @see SOCGame#forceEndTurn()
*/
private final boolean forceEndGameTurn(SOCGame ga, final String plName) {
final String gaName = ga.getName();
final int cpn = ga.getCurrentPlayerNumber();
final int endFromGameState = ga.getGameState();
SOCPlayer cp = ga.getPlayer(cpn);
if (cp.hasAskedSpecialBuild()) {
cp.setAskedSpecialBuild(false);
srv.messageToGame(gaName, new SOCPlayerElement(gaName, cpn, SOCPlayerElement.SET, SOCPlayerElement.ASK_SPECIAL_BUILD, 0));
}
final SOCForceEndTurnResult res = ga.forceEndTurn();
// also could be initial placement (START1A or START2A or START3A).
if (SOCGame.OVER == ga.getGameState())
// <--- Early return: All players have left ---
return false;
/**
* Report any resources lost or gained.
* See also forceGamePlayerDiscardOrGain for same reporting code.
*/
SOCResourceSet resGainLoss = res.getResourcesGainedLost();
if (resGainLoss != null) {
/**
* If gold hex or returning resources to player (not discarding), report actual types/amounts.
* For discard, tell the discarding player's client that they discarded the resources,
* tell everyone else that the player discarded unknown resources.
*/
if (!res.isLoss()) {
if ((endFromGameState == SOCGame.WAITING_FOR_PICK_GOLD_RESOURCE) || (endFromGameState == SOCGame.STARTS_WAITING_FOR_PICK_GOLD_RESOURCE)) {
// Send SOCPlayerElement messages, "gains" text
reportRsrcGainGold(ga, cp, cpn, resGainLoss, true, false);
} else {
// Send SOCPlayerElement messages
reportRsrcGainLoss(gaName, resGainLoss, false, false, cpn, -1, null, null);
}
} else {
Connection c = srv.getConnection(plName);
if ((c != null) && c.isConnected())
reportRsrcGainLoss(gaName, resGainLoss, true, true, cpn, -1, null, c);
int totalRes = resGainLoss.getTotal();
srv.messageToGameExcept(gaName, c, new SOCPlayerElement(gaName, cpn, SOCPlayerElement.LOSE, SOCPlayerElement.UNKNOWN, totalRes, true), true);
// "{0} discarded {1} resources."
srv.messageToGameKeyed(ga, true, "action.discarded", plName, totalRes);
}
}
/**
* report any dev-card or item returned to player's hand
*/
final SOCInventoryItem itemCard = res.getReturnedInvItem();
SOCInventoryItemAction retItemActionMsg = null;
if (itemCard != null) {
Connection c = srv.getConnection(plName);
if ((c != null) && c.isConnected()) {
if (itemCard instanceof SOCDevCard) {
int card = itemCard.itype;
if ((card == SOCDevCardConstants.KNIGHT) && (c.getVersion() < SOCDevCardConstants.VERSION_FOR_NEW_TYPES))
card = SOCDevCardConstants.KNIGHT_FOR_VERS_1_X;
srv.messageToPlayer(c, new SOCDevCardAction(gaName, cpn, SOCDevCardAction.ADD_OLD, card));
} else {
retItemActionMsg = new SOCInventoryItemAction(gaName, cpn, (itemCard.isPlayable() ? SOCInventoryItemAction.ADD_PLAYABLE : SOCInventoryItemAction.ADD_OTHER), itemCard.itype, itemCard.isKept(), itemCard.isVPItem(), itemCard.canCancelPlay);
srv.messageToPlayer(c, retItemActionMsg);
}
}
// Announce item to game with same retItemActionMsg sent to player?
boolean announceAsInvItemAction = false;
// Announce this item to game as an unknown dev card type?
boolean announceAsUnknown = true;
if (!(itemCard instanceof SOCDevCard)) {
// SOCInventoryItem: Add any new kinds here, to announce to all players.
// If it needs a special message, do so and set announceAsUnknown = false
// If it's private and doesn't need a special message, set handled = true and let it announce as unknown
boolean handled = false;
if (ga.isGameOptionSet(SOCGameOption.K_SC_FTRI)) {
// endFromGameState is PLACING_INV_ITEM.
// "Gift port" item details are public, send return message to whole game:
handled = true;
announceAsInvItemAction = true;
announceAsUnknown = false;
}
// Fallback:
if (!handled)
System.err.println("forceEndGameTurn: Unhandled inventory item type " + itemCard.itype + " class " + itemCard.getClass());
}
if (announceAsInvItemAction) {
srv.messageToGameExcept(gaName, c, retItemActionMsg, true);
} else if (announceAsUnknown) {
if (ga.clientVersionLowest >= SOCDevCardConstants.VERSION_FOR_NEW_TYPES) {
srv.messageToGameExcept(gaName, c, new SOCDevCardAction(gaName, cpn, SOCDevCardAction.ADD_OLD, SOCDevCardConstants.UNKNOWN), true);
} else {
srv.messageToGameForVersionsExcept(ga, -1, SOCDevCardConstants.VERSION_FOR_NEW_TYPES - 1, c, new SOCDevCardAction(gaName, cpn, SOCDevCardAction.ADD_OLD, SOCDevCardConstants.UNKNOWN_FOR_VERS_1_X), true);
srv.messageToGameForVersionsExcept(ga, SOCDevCardConstants.VERSION_FOR_NEW_TYPES, Integer.MAX_VALUE, c, new SOCDevCardAction(gaName, cpn, SOCDevCardAction.ADD_OLD, SOCDevCardConstants.UNKNOWN), true);
}
srv.messageToGameKeyed(ga, true, "forceend.devcard.returned", plName);
// "{0}''s just-played development card was returned."
}
}
/**
* For initial placements, we don't end turns as normal.
* (Player number may go forward or backwards, new state isn't ROLL_OR_CARD, etc.)
* Update clients' gamestate, but don't call endGameTurn.
*/
final int forceRes = res.getResult();
if ((forceRes == SOCForceEndTurnResult.FORCE_ENDTURN_SKIP_START_ADV) || (forceRes == SOCForceEndTurnResult.FORCE_ENDTURN_SKIP_START_ADVBACK)) {
if (res.didUpdateFP() || res.didUpdateLP()) {
final int fpn = ga.getFirstPlayer();
final SOCMessage msg = (ga.clientVersionLowest >= SOCGameElements.MIN_VERSION) ? new SOCGameElements(gaName, SOCGameElements.FIRST_PLAYER, fpn) : new SOCFirstPlayer(gaName, fpn);
// will cause clients to recalculate lastPlayer too
srv.messageToGame(gaName, msg);
}
sendTurn(ga, false);
// <--- Early return ---
return true;
}
/**
* If the turn can now end, proceed as if player requested it.
* Otherwise, send current gamestate. We'll all wait for other
* players to send discard messages, and afterwards this turn can end.
*/
if (ga.canEndTurn(cpn))
// could force gamestate to OVER, if a client leaves
endGameTurn(ga, null, true);
else
sendGameState(ga, false, false);
return (ga.getGameState() != SOCGame.OVER);
}
Aggregations