use of soc.message.SOCInventoryItemAction in project JSettlers2 by jdmonin.
the class SOCGameHandler method playerEvent.
/**
* Listener callback for per-player scenario events on the large sea board.
* For example, there might be an SVP awarded for settlements.
* Server sends messages to the game to announce it (PLAYERELEMENT,
* {@link #updatePlayerSVPPendingMessage(SOCGame, SOCPlayer, int, String)}, etc).
*<P>
* <em>Threads:</em> The game's treater thread handles incoming client messages and calls
* game methods that change state. Those same game methods will trigger the scenario events;
* so, the treater thread will also run this <tt>playerEvent</tt> callback.
*
* @param ga Game
* @param pl Player
* @param evt Event code
* @see #gameEvent(SOCGame, SOCScenarioGameEvent, Object)
* @param flagsChanged True if this event changed {@link SOCPlayer#getScenarioPlayerEvents()},
* {@link SOCPlayer#getSpecialVP()}, or another flag documented for <tt>evt</tt> in
* {@link SOCScenarioPlayerEvent}
* @param obj Object related to the event, or null; documented for <tt>evt</tt> in {@link SOCScenarioPlayerEvent}.
* Example: The {@link SOCVillage} for {@link SOCScenarioPlayerEvent#CLOTH_TRADE_ESTABLISHED_VILLAGE}.
* @since 2.0.00
*/
public void playerEvent(final SOCGame ga, final SOCPlayer pl, final SOCScenarioPlayerEvent evt, final boolean flagsChanged, final Object obj) {
// Note: Some SOCGameHandler code assumes that player events are fired only during
// SOCGameMessageHandler.handlePUTPIECE and handleMOVEPIECE.
// Most handle* methods don't check pendingMessagesOut before sending game state.
// If a new player event breaks this assumption, adjust SOCGameHandler.playerEvent(...)
// and related code; search where SOCGame.pendingMessagesOut is used.
final String gaName = ga.getName(), plName = pl.getName();
final int pn = pl.getPlayerNumber();
boolean sendSVP = true;
boolean sendPlayerEventsBitmask = true;
switch(evt) {
case SVP_SETTLED_ANY_NEW_LANDAREA:
{
final String newSettleEventStr = (playerEvent_newSettlementIsByShip(ga, (SOCSettlement) obj)) ? // "growing past the main island"
"event.svp.sc_sany.island" : // "growing to a new area"
"event.svp.sc_sany.area";
updatePlayerSVPPendingMessage(ga, pl, 1, newSettleEventStr);
}
break;
case SVP_SETTLED_EACH_NEW_LANDAREA:
{
final String newSettleEventStr = (playerEvent_newSettlementIsByShip(ga, (SOCSettlement) obj)) ? // "settling a new island"
"event.svp.sc_seac.island" : // "settling a new area"
"event.svp.sc_seac.area";
updatePlayerSVPPendingMessage(ga, pl, 2, newSettleEventStr);
sendPlayerEventsBitmask = false;
final int las = pl.getScenarioSVPLandAreas();
if (las != 0)
ga.pendingMessagesOut.add(new SOCPlayerElement(gaName, pn, SOCPlayerElement.SET, SOCPlayerElement.SCENARIO_SVP_LANDAREAS_BITMASK, las));
}
break;
case CLOTH_TRADE_ESTABLISHED_VILLAGE:
{
sendSVP = false;
if (!flagsChanged)
sendPlayerEventsBitmask = false;
ga.pendingMessagesOut.add(new UnlocalizedString("event.sc_clvi.established", // "{0} established a trade route with a village."
plName));
if (flagsChanged)
srv.messageToPlayerPendingKeyed(pl, gaName, "event.sc_clvi.not.prevented.pirate");
// "You are no longer prevented from moving the pirate ship."
// Player gets 1 cloth for establishing trade
SOCVillage vi = (SOCVillage) obj;
srv.messageToGame(gaName, new SOCPieceValue(gaName, vi.getCoordinates(), vi.getCloth(), 0));
srv.messageToGame(gaName, new SOCPlayerElement(gaName, pn, SOCPlayerElement.SET, SOCPlayerElement.SCENARIO_CLOTH_COUNT, pl.getCloth()));
}
break;
case DEV_CARD_REACHED_SPECIAL_EDGE:
{
sendPlayerEventsBitmask = false;
sendSVP = false;
IntPair edge_cardType = (IntPair) obj;
Connection c = srv.getConnection(plName);
ga.pendingMessagesOut.add(new UnlocalizedString("action.built.sc_ftri.dev", // "{0} gets a Development Card as a gift from the Lost Tribe."
plName));
srv.messageToPlayer(c, new SOCDevCardAction(gaName, pn, SOCDevCardAction.DRAW, edge_cardType.getB()));
srv.messageToGameExcept(gaName, c, new SOCDevCardAction(gaName, pn, SOCDevCardAction.DRAW, SOCDevCardConstants.UNKNOWN), true);
srv.messageToGame(gaName, new SOCSimpleAction(gaName, -1, SOCSimpleAction.BOARD_EDGE_SET_SPECIAL, edge_cardType.getA(), 0));
}
break;
case SVP_REACHED_SPECIAL_EDGE:
{
// "a gift from the Lost Tribe"
updatePlayerSVPPendingMessage(ga, pl, 1, "event.svp.sc_ftri.gift");
sendPlayerEventsBitmask = false;
srv.messageToGame(gaName, new SOCSimpleAction(gaName, -1, SOCSimpleAction.BOARD_EDGE_SET_SPECIAL, ((Integer) obj).intValue(), 0));
}
break;
case REMOVED_TRADE_PORT:
{
sendPlayerEventsBitmask = false;
sendSVP = false;
IntPair edge_portType = (IntPair) obj;
final int edge = edge_portType.getA(), portType = edge_portType.getB();
if ((edge & 0xFF) <= ga.getBoard().getBoardWidth())
// announce removal from board, unless (for debugging)
// this port wasn't really on the board at clients
srv.messageToGame(gaName, new SOCSimpleAction(gaName, pn, SOCSimpleAction.TRADE_PORT_REMOVED, edge, portType));
if (ga.getGameState() == SOCGame.PLACING_INV_ITEM) {
// Removal happens during ship piece placement, which is followed at server with sendGameState.
// When sendGameState gives the new state, client will prompt current player to place now.
// We just need to send the client PLACING_EXTRA, for the port type and not-cancelable flag.
Connection c = srv.getConnection(plName);
srv.messageToPlayer(c, new SOCInventoryItemAction(gaName, pn, SOCInventoryItemAction.PLACING_EXTRA, -portType, false, false, false));
} else {
// port was added to player's inventory;
// if this message changes, also update SOCGameHandler.processDebugCommand_scenario
srv.messageToGame(gaName, new SOCInventoryItemAction(gaName, pn, SOCInventoryItemAction.ADD_PLAYABLE, -portType, false, false, true));
}
}
break;
default:
// Suppress warning; not all enum values need a handler here
break;
}
if (sendSVP)
ga.pendingMessagesOut.add(new SOCPlayerElement(gaName, pn, SOCPlayerElement.SET, SOCPlayerElement.SCENARIO_SVP, pl.getSpecialVP()));
if (sendPlayerEventsBitmask)
ga.pendingMessagesOut.add(new SOCPlayerElement(gaName, pn, SOCPlayerElement.SET, SOCPlayerElement.SCENARIO_PLAYEREVENTS_BITMASK, pl.getScenarioPlayerEvents()));
}
use of soc.message.SOCInventoryItemAction 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);
}
use of soc.message.SOCInventoryItemAction in project JSettlers2 by jdmonin.
the class SOCGameHandler method processDebugCommand_scenario.
/**
* Process any {@code *SCEN*} scenario debug commands.
*
*<H5>Currently recognized commands, per scenario:</H5>
*<UL>
* <LI> <B>{@link SOCGameOption#K_SC_FTRI SC_FTRI}:</B>
* <UL>
* <LI> giveport #typenum #placeflag player
* </UL>
*<UL>
* If you add a debug command, also update {@link #SOC_DEBUG_COMMANDS_HELP}.
*
* @param c Connection (client) sending this message
* @param gaName Game to which this applies
* @param args Debug command string from the user.
* Caller must remove prefix {@link #DEBUG_CMD_PFX_SCENARIO} and then {@link String#trim()}.
* @since 2.0.00
*/
private final void processDebugCommand_scenario(final Connection c, final String gaName, final String argStr) {
if (argStr.length() == 0)
return;
final SOCGame ga = srv.gameList.getGameData(gaName);
if (ga == null)
return;
if (ga.getGameOptionStringValue("SC") == null) {
srv.messageToPlayer(c, gaName, "This game has no scenario");
return;
}
if (!ga.isGameOptionSet(SOCGameOption.K_SC_FTRI)) {
srv.messageToPlayer(c, gaName, "This scenario has no debug commands");
return;
}
// Tokenize the command arguments:
// Don't use string.split("\\s+") because the last argument might be a player name with a space,
// and "un-splitting" isn't easy
StringTokenizer st = new StringTokenizer(argStr);
if (!st.hasMoreTokens())
// unlikely: argStr was already trimmed and then checked length != 0
return;
final String subCmd = st.nextToken();
// _SC_FTRI debug commands:
if (subCmd.equalsIgnoreCase("giveport")) {
// giveport #typenum #placeflag player
boolean parseOK = false;
int ptype = 0;
boolean placeNow = false;
SOCPlayer pl = null;
try {
ptype = Integer.parseInt(st.nextToken());
int i = Integer.parseInt(st.nextToken());
placeNow = (i == 1);
if (// must be 0 or 1
placeNow || (i == 0)) {
parseOK = (ptype >= SOCBoard.MISC_PORT) && (ptype <= SOCBoard.WOOD_PORT);
if (parseOK) {
// get all of the rest for player name, by choosing an unlikely delimiter character
String plName = st.nextToken(Character.toString((char) 1)).trim();
pl = debug_getPlayer(c, ga, plName);
if (pl == null)
// debug_getPlayer has sent not-found message
return;
}
}
} catch (NumberFormatException e) {
}// not enough tokens; can occur at name when parseOK.
catch (NoSuchElementException e) {
parseOK = false;
}
if (!parseOK) {
srv.messageToPlayer(c, gaName, "### Usage: giveport #typenum #placeflag player");
srv.messageToPlayer(c, gaName, "### typenum: 0 for 3:1 port, or 1 to 5 (clay, ore, sheep, wheat, wood)");
srv.messageToPlayer(c, gaName, "### placeflag: 1 to force placement now, 0 to add to inventory");
return;
}
// Message-send logic is from playerEvent(..).
if (placeNow) {
if ((ga.getCurrentPlayerNumber() != pl.getPlayerNumber()) || (pl.getPortMovePotentialLocations(false) == null)) {
srv.messageToPlayer(c, gaName, "Player must be current and have a potential location for the port");
return;
}
// Fake placement off-board so we can call ga.removePort,
// which will handle game states and notification, at a
// vertical edge just past the side of the board: 0x113, 0x115, ...
final int edge = (ga.getBoard().getBoardWidth() + 2) | 0x101;
ga.placePort(null, edge, ptype);
ga.removePort(pl, edge);
// removePort calls scenarioEventListener.playerEvent(REMOVED_TRADE_PORT),
// which sends some messages but not GAMESTATE
sendGameState(ga);
} else {
pl.getInventory().addItem(SOCInventoryItem.createForScenario(ga, -ptype, true, false, false, !placeNow));
srv.messageToGame(gaName, new SOCInventoryItemAction(gaName, pl.getPlayerNumber(), SOCInventoryItemAction.ADD_PLAYABLE, -ptype, false, false, true));
}
} else {
srv.messageToPlayer(c, gaName, "Unknown debug command: " + subCmd);
}
}
Aggregations