use of soc.util.IntPair 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.util.IntPair in project JSettlers2 by jdmonin.
the class SOCGame method removePort.
/**
* For scenario option {@link SOCGameOption#K_SC_FTRI _SC_FTRI}, remove a "gift" port
* at this edge to be placed elsewhere. Assumes {@link #canRemovePort(SOCPlayer, int)} has already
* been called to validate player, edge, and game state.
*<P>
* This method will remove the port from the board. At server it will also either add it to the
* player's inventory or set the game's placingItem field; change the game state if placing
* it immediately; and then fire {@link SOCScenarioPlayerEvent#REMOVED_TRADE_PORT}.
*<P>
* <b>At the server:</b> Not called directly; called only from other game/player methods.
* Ports are currently removed only by a player's adjacent ship placement, so
* {@link SOCPlayer#putPiece(SOCPlayingPiece, boolean)} would eventually call this
* method if {@code canRemovePort(..)}. The server calls
* {@link SOCPlayer#getPortMovePotentialLocations(boolean) pl.getPortMovePotentialLocations(false)}
* to determine if immediate placement is possible and thus required.
*<P>
* In the PlayerEvent handler or after this method returns, check
* {@link #getGameState()} == {@link #PLACING_INV_ITEM} to see whether the port must immediately
* be placed, or was instead added to the player's inventory.
*<P>
* <b>At the client:</b> Call this method to update the board data and player port flags.
* The server will send other messages about game state and player inventory. If those set
* {@link #getGameState()} to {@link #PLACING_INV_ITEM}, the current player must choose a location for
* port placement: Call
* {@link SOCPlayer#getPortMovePotentialLocations(boolean) SOCPlayer.getPortMovePotentialLocations(true)}
* to present options to the user. The user will choose an edge and send a request to the server. The server
* will validate and if valid call {@link #placePort(int)} and send updated status to the game's clients.
*
* @param pl Player who is removing the port: Must be current player. Ignored at client, {@code null} is okay there.
* @param edge Port's edge coordinate
* @throws UnsupportedOperationException if ! {@link #hasSeaBoard}
* @throws NullPointerException if {@code pl} is null at server
* @return The port removed, with its {@link SOCInventoryItem#itype} in range
* -{@link SOCBoard#WOOD_PORT WOOD_PORT} to -{@link SOCBoard#MISC_PORT MISC_PORT}
*/
public SOCInventoryItem removePort(SOCPlayer pl, final int edge) throws UnsupportedOperationException, NullPointerException {
if (!hasSeaBoard)
throw new UnsupportedOperationException();
final int ptype = ((SOCBoardLarge) board).removePort(edge);
for (int pn = 0; pn < maxPlayers; ++pn) players[pn].updatePortFlagsAfterRemove(ptype, true);
if ((pl == null) || !isAtServer)
pl = players[currentPlayerNumber];
// note: if this logic changes, also update SOCGameHandler.processDebugCommand_scenario
final boolean placeNow = (pl.getPortMovePotentialLocations(false) != null);
final SOCInventoryItem port = SOCInventoryItem.createForScenario(this, -ptype, true, false, false, !placeNow);
if (isAtServer) {
if (!placeNow) {
pl.getInventory().addItem(port);
} else {
placingItem = port;
oldGameState = (gameState == SPECIAL_BUILDING) ? SPECIAL_BUILDING : PLAY1;
gameState = PLACING_INV_ITEM;
}
// Fire the scenario player event, with the removed port's edge coord and type
if (scenarioEventListener != null) {
scenarioEventListener.playerEvent(this, pl, SOCScenarioPlayerEvent.REMOVED_TRADE_PORT, false, new IntPair(edge, ptype));
}
}
return port;
}
use of soc.util.IntPair in project JSettlers2 by jdmonin.
the class SOCPlayer method calcLongestRoad2.
/**
* Calculates the longest road / longest trade route for this player
*
* @return the length of the longest road for this player
*/
public int calcLongestRoad2() {
// Date startTime = new Date();
//
// clear the lr paths vector so that we have an accurate
// representation. if someone cut our longest path in two
// we won't catch it unless we clear the vector
//
// D.ebugPrintln("CLEARING PATH DATA");
lrPaths.removeAllElements();
/**
* we're doing a depth first search of all possible road paths.
* For similar code, see soc.robot.SOCRobotDM.recalcLongestRoadETAAux.
* Both methods rely on a stack holding NodeLenVis (pop to curNode in loop);
* they differ in actual element type within the stack because they are
* gathering slightly different results (length or a stack of edges).
*/
SOCBoard board = game.getBoard();
Stack<NodeLenVis<IntPair>> pending = new Stack<NodeLenVis<IntPair>>();
int longest = 0;
for (Integer rn : roadNodes) {
final int pathStartNodeCoord = rn.intValue();
pending.push(new NodeLenVis<IntPair>(pathStartNodeCoord, 0, new Vector<IntPair>()));
while (!pending.isEmpty()) {
NodeLenVis<IntPair> curNode = pending.pop();
final int coord = curNode.node;
final int len = curNode.len;
Vector<IntPair> visited = curNode.vis;
boolean pathEnd = false;
final SOCPlayingPiece settlementAtNodeCoord;
/**
* check for road blocks
*/
if (len > 0) {
settlementAtNodeCoord = board.settlementAtNode(coord);
if ((settlementAtNodeCoord != null) && (settlementAtNodeCoord.getPlayerNumber() != playerNumber)) {
pathEnd = true;
// D.ebugPrintln("^^^ path end at "+Integer.toHexString(coord));
}
} else {
settlementAtNodeCoord = null;
}
if (!pathEnd) {
/**
* Check if this road path continues to adjacent connected nodes.
*/
// may be set false in loop
pathEnd = true;
final int[] adjacNodes = board.getAdjacentNodesToNode_arr(coord);
for (int ni = adjacNodes.length - 1; ni >= 0; --ni) {
final int j = adjacNodes[ni];
if (j == -9)
continue;
if (isConnectedByRoad(coord, j)) {
// sea board: road/ship from node to j
final SOCRoad roadFromNode;
if (game.hasSeaBoard) {
// Check for road<->ship transitions,
// which require a settlement/city at node.
// If len==0, inboundRoad is null because we're just starting.
roadFromNode = getRoadOrShip(board.getEdgeBetweenAdjacentNodes(coord, j));
if (len > 0) {
if (// shouldn't happen
roadFromNode == null)
continue;
if ((roadFromNode.isRoadNotShip() != curNode.inboundRoad.isRoadNotShip()) && (settlementAtNodeCoord == null)) {
// Requires settlement/city to connect road to ship
continue;
}
}
} else {
roadFromNode = null;
}
IntPair pair = new IntPair(coord, j);
boolean match = false;
for (IntPair vis : visited) {
if (vis.equals(pair)) {
match = true;
break;
}
}
if (!match) {
Vector<IntPair> newVis = new Vector<IntPair>(visited);
newVis.addElement(pair);
pending.push(new NodeLenVis<IntPair>(j, len + 1, newVis, roadFromNode));
pathEnd = false;
}
}
}
// foreach(adjacNodes)
}
if (pathEnd) {
if (len > longest) {
longest = len;
}
//
// we want to store the longest path for a single set of nodes
// check to make sure that we don't save two paths that share a node
//
boolean intersection;
boolean addNewPath = true;
Vector<SOCLRPathData> trash = new Vector<SOCLRPathData>();
for (SOCLRPathData oldPathData : lrPaths) {
// D.ebugPrintln("oldPathData = " + oldPathData);
Vector<IntPair> nodePairs = oldPathData.getNodePairs();
intersection = false;
for (IntPair vis : visited) {
for (IntPair np : nodePairs) {
if (np.equals(vis)) {
// D.ebugPrintln("oldPathData.nodePairs.contains(vis)");
intersection = true;
break;
}
}
if (intersection) {
break;
}
}
if (intersection) {
//
if (oldPathData.getLength() < len) {
// D.ebugPrintln("REMOVING OLDPATHDATA");
trash.addElement(oldPathData);
} else {
addNewPath = false;
// D.ebugPrintln("NOT ADDING NEW PATH");
}
}
}
if (!trash.isEmpty()) {
for (SOCLRPathData oldPathData : trash) {
lrPaths.removeElement(oldPathData);
}
}
if (addNewPath) {
SOCLRPathData newPathData = new SOCLRPathData(pathStartNodeCoord, coord, len, visited);
// D.ebugPrintln("ADDING PATH: " + newPathData);
lrPaths.addElement(newPathData);
}
}
}
}
longestRoadLength = longest;
// System.out.println("LONGEST FOR "+name+" IS "+longest+" TIME = "+elapsed+"ms");
return longest;
}
use of soc.util.IntPair in project JSettlers2 by jdmonin.
the class SOCPlayerNumbers method undoAddNumberForResource.
/**
* remove a number for a resource
* do this when you take back a piece
*
* @param number the dice-roll number
* @param resource the resource, in range {@link SOCResourceConstants#CLAY} to {@link SOCResourceConstants#WOOD},
* from {@link SOCBoard#getHexTypeFromCoord(int)}.
* If {@link #hasSeaBoard}, can be {@link SOCBoardLarge#GOLD_HEX}.
* @param hex the hex coordinate ID
*/
public void undoAddNumberForResource(int number, int resource, int hex) {
if ((resource >= SOCResourceConstants.CLAY) && (resource <= SOCResourceConstants.WOOD)) {
for (Integer num : numbersForResource[resource]) {
if (num.intValue() == number) {
numbersForResource[resource].removeElement(num);
break;
}
}
for (Integer resourceInt : resourcesForNumber[number]) {
if (resourceInt.intValue() == resource) {
resourcesForNumber[number].removeElement(resourceInt);
break;
}
}
} else {
if (!(hasSeaBoard && (resource == SOCBoardLarge.GOLD_HEX))) {
// <--- Ignore all other resource/hex types ---
return;
}
for (int res = SOCResourceConstants.CLAY; res <= SOCResourceConstants.WOOD; ++res) {
Iterator<Integer> numIter = numbersForResource[res].iterator();
while (numIter.hasNext()) {
final int num = numIter.next().intValue();
if (num == number) {
numIter.remove();
break;
}
}
}
// range CLAY to WOOD
boolean[] removed = new boolean[SOCResourceConstants.UNKNOWN];
Iterator<Integer> resIter = resourcesForNumber[number].iterator();
while (resIter.hasNext()) {
final int res = resIter.next().intValue();
if (!removed[res]) {
resIter.remove();
removed[res] = true;
}
}
// GOLD_HEX will be in numberAndResourceForHex.
}
Vector<IntPair> pairs = numberAndResourceForHex.get(Integer.valueOf(hex));
if (pairs != null) {
for (IntPair numAndResource : pairs) {
if ((numAndResource.getA() == number) && (numAndResource.getB() == resource)) {
pairs.removeElement(numAndResource);
break;
}
}
}
}
use of soc.util.IntPair in project JSettlers2 by jdmonin.
the class SOCPlayerNumbers method addNumberForResource.
/**
* add a number to the list of dice numbers for a resource
*
* @param diceNum the dice-roll number
* @param resource the resource, in range {@link SOCResourceConstants#CLAY} to {@link SOCResourceConstants#WOOD};
* resources outside this range are ignored.
* If {@link #hasSeaBoard}, can be {@link SOCBoardLarge#GOLD_HEX}
* as returned from {@link SOCBoardLarge#getHexTypeFromCoord(int)}.
* @param hex the hex coordinate ID
*/
public void addNumberForResource(final int diceNum, final int resource, final int hex) {
if ((resource >= SOCResourceConstants.CLAY) && (resource <= SOCResourceConstants.WOOD)) {
numbersForResource[resource].addElement(new Integer(diceNum));
Integer resourceInt = new Integer(resource);
// if (!resourcesForNumber[number].contains(resourceInt)) {
resourcesForNumber[diceNum].addElement(resourceInt);
// }
} else {
if (!(hasSeaBoard && (resource == SOCBoardLarge.GOLD_HEX))) {
// <--- Ignore all other resource/hex types ---
return;
}
// GOLD_HEX: Add all 5 resource types
final Integer diceNumInt = new Integer(diceNum);
for (int res = SOCResourceConstants.CLAY; res <= SOCResourceConstants.WOOD; ++res) {
numbersForResource[res].addElement(diceNumInt);
resourcesForNumber[diceNum].addElement(new Integer(res));
}
// GOLD_HEX is okay in numberAndResourceForHex.
}
final Integer hexInt = new Integer(hex);
Vector<IntPair> pairs = numberAndResourceForHex.get(hexInt);
if (pairs == null) {
pairs = new Vector<IntPair>();
numberAndResourceForHex.put(hexInt, pairs);
}
pairs.addElement(new IntPair(diceNum, resource));
}
Aggregations