This is RiskGameManager.m in view mode; [Download] [Up]
//
// This file is a part of Risk by Mike Ferris.
//
#import "Risk.h"
RCSID ("$Id: RiskGameManager.m,v 1.7 1997/12/18 21:03:46 nygard Exp $");
#import "RiskGameManager.h"
#import "ArmyPlacementValidator.h"
#import "ArmyView.h"
#import "BoardSetup.h"
#import "CardPanelController.h"
#import "CardSet.h"
#import "Country.h"
#import "DiceInspector.h"
#import "GameConfiguration.h"
#import "RiskCard.h"
#import "RiskMapView.h"
#import "RiskPlayer.h"
#import "RiskWorld.h"
#import "SNRandom.h"
#import "StatusView.h"
#import "WorldInfoController.h"
//======================================================================
// The RiskGameManager controls most of the game play. It notifies
// the players of the various phases of game play, and does some
// checking of messages to try to limit invalid actions by players (or
// some cheating.)
//======================================================================
DEFINE_NSSTRING (RGMGameOverNotification);
#define AGSReason(state1) [NSString stringWithFormat:@"Current game state is (Player %d, %@). Expected game state to be %@.", currentPlayerNumber, NSStringFromGameState (gameState), NSStringFromGameState (state1)]
#define AGSReason2(state1, state2) [NSString stringWithFormat:@"Current game state is (Player %d, %@). Expected game state to be %@ or %@.", currentPlayerNumber, NSStringFromGameState (gameState), NSStringFromGameState (state1), NSStringFromGameState (state2)]
#define AGSReason3(state1, state2, state3) [NSString stringWithFormat:@"Current game state is (Player %d, %@). Expected game state to be %@, %@ or %@.", currentPlayerNumber, NSStringFromGameState (gameState), NSStringFromGameState (state1), NSStringFromGameState (state2), NSStringFromGameState (state3)]
#define AGSReason4(state1, state2, state3, state4) [NSString stringWithFormat:@"Current game state is (Player %d, %@). Expected game state to be %@, %@, %@ or %@.", currentPlayerNumber, NSStringFromGameState (gameState), NSStringFromGameState (state1), NSStringFromGameState (state2), NSStringFromGameState (state3), NSStringFromGameState (state4)]
#define AssertGameState(state1) NSAssert1 (gameState == state1, @"%@", AGSReason (state1))
#define AssertGameState2(state1, state2) NSAssert1 (gameState == state1 || gameState == state2, @"%@", AGSReason2 (state1, state2))
#define AssertGameState3(state1, state2, state3) NSAssert1 (gameState == state1 || gameState == state2 || gameState == state3, @"%@", AGSReason3 (state1, state2, state3))
#define AssertGameState4(state1, state2, state3, state4) NSAssert1 (gameState == state1 || gameState == state2 || gameState == state3 || gameState == state4, @"%@", AGSReason4 (state1, state2, state3, state4))
#define RiskGameManager_VERSION 1
@implementation RiskGameManager
+ (void) initialize
{
if (self == [RiskGameManager class])
{
[self setVersion:RiskGameManager_VERSION];
}
}
//----------------------------------------------------------------------
- init
{
NSString *nibFile;
BOOL loaded;
int l;
if ([super init] == nil)
return nil;
world = nil;
mapView = nil;
phaseComputerMove = nil;
phasePlaceArmies = nil;
phaseAttack = nil;
phaseFortify = nil;
phaseChooseCountries = nil;
currentPhaseView = nil;
rng = [[SNRandom instance] retain];
nibFile = @"GameBoard.nib";
loaded = [NSBundle loadNibNamed:nibFile owner:self];
if (loaded == NO)
{
NSLog (@"Could not load %@.", nibFile);
[super dealloc];
return nil;
}
configuration = [[GameConfiguration alloc] init];;
activePlayerCount = 0;
for (l = 0; l < MAX_PLAYERS; l++)
{
players[l] = nil;
playersActive[l] = NO;
}
initialArmyCount = 0;
gameState = gs_no_game;
currentPlayerNumber = 0;
cardPanelController = nil;
cardDeck = nil;
discardDeck = [[NSMutableArray array] retain];
playerHasConqueredCountry = NO;
armyPlacementValidator = nil;
nextCardSetValue = 4;
diceInspector = nil;
worldInfoController = nil;
toolMenu = [[[NSApp mainMenu] itemWithTitle:@"Tools"] target];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector (defaultsChanged:)
name:RiskBoardSetupPlayerColorsChangedNotification
object:nil];
return self;
}
//----------------------------------------------------------------------
// Never really gets dealloc'd
- (void) dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[self stopGame];
SNRelease (world);
SNRelease (phaseComputerMove);
SNRelease (phasePlaceArmies);
SNRelease (phaseAttack);
SNRelease (phaseFortify);
SNRelease (phaseChooseCountries);
SNRelease (cardDeck);
SNRelease (discardDeck);
SNRelease (cardPanelController);
SNRelease (armyPlacementValidator);
SNRelease (diceInspector);
SNRelease (worldInfoController);
SNRelease (rng);
[super dealloc];
}
//----------------------------------------------------------------------
- (void) awakeFromNib
{
NSView *tmp1, *tmp2;
// The windows should not be visible at launch time, so we can
// move them to the default locations.
[mapWindow setFrameAutosaveName:[mapWindow title]];
[mapWindow orderFront:self];
[controlPanel setFrameAutosaveName:[controlPanel title]];
[controlPanel orderFront:self];
[[phaseComputerMove retain] removeFromSuperview];
[[phasePlaceArmies retain] removeFromSuperview];
[[phaseAttack retain] removeFromSuperview];
[[phaseFortify retain] removeFromSuperview];
[[phaseChooseCountries retain] removeFromSuperview];
// Try to make sure the map view doesn't obscure any peer
// views when it redraws. It must also set it's superview
// to need display whenever it needs display.
tmp1 = [mapView superview];
tmp2 = [mapView retain];
[tmp2 removeFromSuperview];
[tmp1 addSubview:tmp2 positioned:NSWindowBelow relativeTo:nil];
[tmp2 release];
#ifdef __APPLE_CPP__
// We don't want to have to validate the items like we do menu items.
[attackMethodPopup setAutoenablesItems:NO];
#endif
}
//----------------------------------------------------------------------
- (void) _logGameState
{
NSString *str;
switch (gameState)
{
case gs_no_game:
str = @"No game";
break;
case gs_establishing_game:
str = @"Establishing game.";
break;
case gs_choose_countries:
str = [NSString stringWithFormat:@"Choose countries -- player %d.", currentPlayerNumber];
break;
case gs_place_initial_armies:
str = [NSString stringWithFormat:@"Place initial armies -- player %d.", currentPlayerNumber];
break;
case gs_place_armies:
str = [NSString stringWithFormat:@"Place armies -- player %d.", currentPlayerNumber];
break;
case gs_attack:
str = [NSString stringWithFormat:@"Attack -- player %d.", currentPlayerNumber];
break;
case gs_move_attacking_armies:
str = [NSString stringWithFormat:@"Move attacking armies -- player %d.", currentPlayerNumber];
break;
case gs_fortify:
str = [NSString stringWithFormat:@"Fortify -- player %d.", currentPlayerNumber];
break;
case gs_place_fortifying_armies:
str = [NSString stringWithFormat:@"Place fortifying armies -- player %d.", currentPlayerNumber];
break;
default:
str = @"<Unknown>.";
break;
}
NSLog (@"-- Game state: %@.", str);
}
//----------------------------------------------------------------------
- (void) showControlPanel:sender
{
[controlPanel orderFront:nil];
}
//----------------------------------------------------------------------
- (void) showDiceInspector:sender
{
if (diceInspector == nil)
{
diceInspector = [[DiceInspector alloc] init];
}
[diceInspector showPanel];
}
//----------------------------------------------------------------------
- (void) showWorldInfoPanel:sender
{
if (worldInfoController == nil)
{
worldInfoController = [[WorldInfoController alloc] init];
[worldInfoController setWorld:world];
}
[worldInfoController showPanel];
}
//----------------------------------------------------------------------
- (BOOL) validateMenuItem:(NSMenuItem *)menuCell
{
SEL action;
BOOL valid;
int tag;
valid = NO;
action = [menuCell action];
tag = [menuCell tag];
if (action == @selector (showPlayerConsole:))
{
NSAssert (tag > 0 && tag < MAX_PLAYERS, @"Tag for player number out of range.");
if (players[tag] != nil)
valid = YES;
}
else if (action == @selector (showControlPanel:)
|| action == @selector (showDiceInspector:)
|| action == @selector (showWorldInfoPanel:))
{
valid = YES;
}
return valid;
}
//----------------------------------------------------------------------
// Set the country name in the map view. Forward event to current player.
// This is the delegate method of RiskMapView.
- (void) mouseDown:(NSEvent *)theEvent inCountry:(Country *)aCountry
{
[countryNameTextField setStringValue:[aCountry countryName]];
if (players[currentPlayerNumber] != nil)
[players[currentPlayerNumber] mouseDown:theEvent inCountry:aCountry];
}
//======================================================================
// General access to world data
//======================================================================
- (RiskWorld *) world
{
return world;
}
//----------------------------------------------------------------------
// Set the world to be used for the game.
- (void) setWorld:(RiskWorld *)newWorld
{
// Can't change world while game is in progress.
AssertGameState (gs_no_game);
SNRelease (world);
world = [newWorld retain];
[mapView setCountryArray:[[world allCountries] allObjects]];
SNRelease (armyPlacementValidator);
armyPlacementValidator = [[ArmyPlacementValidator alloc] initWithRiskWorld:world];
}
//----------------------------------------------------------------------
- (GameConfiguration *) gameConfiguration
{
return configuration;
}
//----------------------------------------------------------------------
- (void) setGameConfiguration:(GameConfiguration *)newGameConfiguration
{
// Can't change the rules of a game in progress.
AssertGameState (gs_no_game);
SNRelease (configuration);
configuration = [newGameConfiguration retain];
}
//----------------------------------------------------------------------
- (GameState) gameState
{
return gameState;
}
//======================================================================
// For status view.
//======================================================================
- (BOOL) isPlayerActive:(Player)number
{
NSAssert (number > 0 && number < MAX_PLAYERS, @"Player number out of range.");
return playersActive[number];
}
//----------------------------------------------------------------------
- (Player) currentPlayerNumber
{
return currentPlayerNumber;
}
//----------------------------------------------------------------------
- (int) activePlayerCount
{
return activePlayerCount;
}
//----------------------------------------------------------------------
- (RiskPlayer *) playerNumber:(Player)number
{
NSAssert (number > 0 && number < MAX_PLAYERS, @"Player number out of range.");
return players[number];
}
//======================================================================
// Player menu support
//======================================================================
- (void) showPlayerConsole:sender
{
int tag;
tag = [sender tag];
NSAssert (tag > 0 && tag < MAX_PLAYERS, @"Tag for player number out of range.");
if (players[tag] != nil)
[players[tag] showConsolePanel:self];
}
//======================================================================
// Establish Game
//======================================================================
- (void) startNewGame
{
NSEnumerator *countryEnumerator;
Country *country;
NSAssert ([self gameInProgress] == NO, @"Game already in progress.");
countryEnumerator = [[world allCountries] objectEnumerator];
[[mapView window] disableFlushWindow];
while (country = [countryEnumerator nextObject])
{
[country setPlayerNumber:0];
}
//[mapView display]; // This is because drawCountry: doesn't draw the background... and it probably should.
[[mapView window] enableFlushWindow];
gameState = gs_establishing_game;
// Set up card and discard decks.
cardDeck = [[world cards] mutableCopy];
nextCardSetValue = 4;
}
//----------------------------------------------------------------------
- (BOOL) addPlayer:(RiskPlayer *)aPlayer number:(Player)number
{
// Can only add players while establishing a new game.
AssertGameState (gs_establishing_game);
NSAssert (players[number] == nil, @"Already have a player in that slot.");
//NSAssert ([type isKindOfClass:[RiskPlayer class]] == YES, @"Player class must be a subclass of RiskPlayer.");
players[number] = [aPlayer retain];
playersActive[number] = YES;
activePlayerCount++;
[players[number] setPlayerToolMenu:[[toolMenu itemWithTitle:[NSString stringWithFormat:@"Player %d", number]] target]];
return YES;
}
//----------------------------------------------------------------------
- (void) beginGame
{
// Can't begin a game that hasn't been established
AssertGameState (gs_establishing_game);
// Calculate initial army count
initialArmyCount = RiskInitialArmyCountForPlayers (activePlayerCount);
[self tryToStart];
}
//----------------------------------------------------------------------
- (void) tryToStart
{
// Turn done...
[self endTurn];
}
//----------------------------------------------------------------------
- (void) stopGame
{
int l;
gameState = gs_no_game;
for (l = 1; l < MAX_PLAYERS; l++)
{
[self deactivatePlayerNumber:l];
}
activePlayerCount = 0;
currentPlayerNumber = 0;
// cardDeck will be a mutable copy of the world cards.
SNRelease (cardDeck);
[discardDeck removeAllObjects];
if (currentPhaseView != nil)
{
[currentPhaseView removeFromSuperview];
currentPhaseView = nil;
}
[[NSNotificationCenter defaultCenter] postNotificationName:RGMGameOverNotification
object:self];
}
//======================================================================
// Game State
//======================================================================
- (BOOL) gameInProgress
{
return gameState != gs_no_game;
}
//----------------------------------------------------------------------
- (void) enteringChooseCountriesPhase
{
currentPlayerNumber = 0;
while ([self nextActivePlayer] == NO)
{
[players[currentPlayerNumber] willBeginChoosingCountries];
}
}
//----------------------------------------------------------------------
- (void) leavingChooseCountriesPhase
{
currentPlayerNumber = 0;
while ([self nextActivePlayer] == NO)
{
[players[currentPlayerNumber] willEndChoosingCountries];
}
}
//----------------------------------------------------------------------
- (void) enteringInitialArmyPlacementPhase
{
currentPlayerNumber = 0;
while ([self nextActivePlayer] == NO)
{
[players[currentPlayerNumber] willBeginPlacingInitialArmies];
}
}
//----------------------------------------------------------------------
- (void) leavingInitialArmyPlacementPhase
{
currentPlayerNumber = 0;
while ([self nextActivePlayer] == NO)
{
[players[currentPlayerNumber] willEndPlacingInitialArmies];
}
}
//----------------------------------------------------------------------
// This method changes the game state to the next state, and sets up the
// user interface elements for that state. The normal
// state progression is:
//
// No Game -> Establishing Game -> Choose Countries -> Place Initial Armies
// -> Place Armies -> Attack -> Fortify
// -> Place Armies -> Attack -> Fortify
// -> Place Armies -> Attack -> Fortify ...
//
//
// In addition, there are special methods to enter other game states.
//----------------------------------------------------------------------
// Option to skip fortify phase?
- (void) endTurn
{
BOOL isInteractivePlayer;
NSView *newPhaseView;
FortifyRule fortifyRule;
int count, tmp;
NSAssert (gameState != gs_no_game /*&& gameState != gs_establishing_game*/, @"No game in progess.");
//[self _logGameState];
// 1. Determine next phase.
// 2. Setup UI elements for that phase (switched view).
// 3. Initiate the phase.
// Determine next phase.
switch (gameState)
{
case gs_establishing_game:
gameState = gs_choose_countries;
if ([configuration initialCountryDistribution] == RandomlyChosen)
{
[self randomlyChooseCountriesForActivePlayers];
gameState = gs_place_initial_armies;
[self enteringInitialArmyPlacementPhase];
}
else
{
[self enteringChooseCountriesPhase];
}
currentPlayerNumber = 0;
[self nextActivePlayer];
break;
case gs_choose_countries:
if ([[self unoccupiedCountries] count] > 0)
{
[mapView selectCountry:nil];
[self nextActivePlayer];
}
else
{
[self leavingChooseCountriesPhase];
gameState = gs_place_initial_armies;
[self enteringInitialArmyPlacementPhase];
currentPlayerNumber = 0;
[self nextActivePlayer];
}
break;
case gs_place_initial_armies:
[mapView selectCountry:nil];
if ([self nextActivePlayer] == YES)
{
initialArmyCount -= [configuration armyPlacementCount];
}
//NSLog (@"initial army count: %d", initialArmyCount);
if (initialArmyCount < 1)
{
[self leavingInitialArmyPlacementPhase];
gameState = gs_place_armies;
currentPlayerNumber = 0;
[self nextActivePlayer];
[players[currentPlayerNumber] willBeginTurn];
[self setArmiesLeftToPlace:[self earnedArmyCountForPlayer:currentPlayerNumber]];
}
break;
case gs_place_armies:
if (armiesLeftToPlace > 0)
{
NSLog (@"Player %d has %d unplaced armies.", currentPlayerNumber, armiesLeftToPlace);
}
gameState = gs_attack;
break;
case gs_attack:
if (playerHasConqueredCountry == YES)
{
// Deal a card to the player.
[self dealCardToPlayerNumber:currentPlayerNumber];
}
gameState = gs_fortify;
armiesBefore = [self totalTroopsForPlayerNumber:currentPlayerNumber];
//NSLog (@"Attack->Fortify, player %d, armies: %d", currentPlayerNumber, armiesBefore);
break;
case gs_move_attacking_armies:
// Go back to the attack phase:
if ([[players[currentPlayerNumber] playerCards] count] > 4)
{
// Force the player to turn in cards.
gameState = gs_place_armies;
[self setArmiesLeftToPlace:0];
}
else
{
gameState = gs_attack;
}
break;
case gs_fortify:
tmp = [self totalTroopsForPlayerNumber:currentPlayerNumber];
//NSLog (@"Fortify->next, Player %d: armies before = %d, armies now = %d", currentPlayerNumber, armiesBefore, tmp);
if (armiesBefore != tmp)
{
NSLog (@"!!Player %d: armies before = %d, armies now = %d", currentPlayerNumber, armiesBefore, tmp);
}
[players[currentPlayerNumber] willEndTurn];
gameState = gs_place_armies;
[self nextActivePlayer];
[players[currentPlayerNumber] willBeginTurn];
[self setArmiesLeftToPlace:[self earnedArmyCountForPlayer:currentPlayerNumber]];
break;
case gs_place_fortifying_armies:
fortifyRule = [configuration fortifyRule];
if (fortifyRule == OneToOneNeighbor || fortifyRule == OneToManyNeighbors)
{
tmp = [self totalTroopsForPlayerNumber:currentPlayerNumber];
//NSLog (@"PlaceFortify->next, Player %d: armies before = %d, armies now = %d",currentPlayerNumber, armiesBefore, tmp);
if (armiesBefore != tmp)
{
NSLog (@"!!Player %d: armies before = %d, armies now = %d", currentPlayerNumber, armiesBefore, tmp);
}
[players[currentPlayerNumber] willEndTurn];
gameState = gs_place_armies;
[self nextActivePlayer];
[players[currentPlayerNumber] willBeginTurn];
[self setArmiesLeftToPlace:[self earnedArmyCountForPlayer:currentPlayerNumber]];
}
else
{
gameState = gs_fortify;
}
break;
default:
NSLog (@"Invalid game state.");
}
//[self _logGameState];
//NSLog (@"active player count: %d", activePlayerCount);
//------------------------------------------------------------
if (currentPhaseView != nil)
{
[currentPhaseView removeFromSuperview];
currentPhaseView = nil;
}
if (activePlayerCount < 2)
{
//NSLog (@"Will not continue with game...");
return;
}
isInteractivePlayer = [players[currentPlayerNumber] isInteractive];
newPhaseView = nil;
//[controlPanel makeMainWindow];
if ([players[currentPlayerNumber] isInteractive] == YES)
{
[[mapView window] makeKeyWindow];
}
// Update phase controls for this new phase:
switch (gameState)
{
case gs_choose_countries:
newPhaseView = (isInteractivePlayer == YES) ? phaseChooseCountries : phaseComputerMove;
break;
case gs_place_initial_armies:
newPhaseView = (isInteractivePlayer == YES) ? phasePlaceArmies : phaseComputerMove;
// Do this here to avoid a little bit of flicker.
[initialArmiesLeftTextField setIntValue:initialArmyCount];
count = [configuration armyPlacementCount];
if (initialArmyCount < count)
count = initialArmyCount;
[self setArmiesLeftToPlace:count];
[armyPlacementValidator placeInAnyCountryForPlayerNumber:currentPlayerNumber];
// You can always review your cards, even if you have none.
if ([players[currentPlayerNumber] canTurnInCardSet] == YES)
[turnInCardsButton setEnabled:YES];
else
[turnInCardsButton setEnabled:NO];
break;
case gs_place_armies:
[mapView selectCountry:nil];
newPhaseView = (isInteractivePlayer == YES) ? phasePlaceArmies : phaseComputerMove;
[initialArmiesLeftTextField setStringValue:@"--"];
[armyPlacementValidator placeInAnyCountryForPlayerNumber:currentPlayerNumber];
// You can always review your cards, even if you have none.
if ([players[currentPlayerNumber] canTurnInCardSet] == YES)
[turnInCardsButton setEnabled:YES];
else
[turnInCardsButton setEnabled:NO];
break;
case gs_attack:
newPhaseView = (isInteractivePlayer == YES) ? phaseAttack : phaseComputerMove;
[self takeAttackMethodFromPlayerNumber:currentPlayerNumber];
[attackingFromTextField setStringValue:@""];
break;
case gs_fortify:
newPhaseView = (isInteractivePlayer == YES) ? phaseFortify : phaseComputerMove;
break;
case gs_move_attacking_armies:
case gs_place_fortifying_armies:
// These states are entered in a different manner.
default:
NSLog (@"Invalid game state.");
}
// Now set up player stuff in "Turn Phase" box:
[nameTextField setStringValue:[players[currentPlayerNumber] playerName]];
[playerColorWell setColor:[[BoardSetup instance] colorForPlayer:currentPlayerNumber]];
[self updatePhaseBox];
if (newPhaseView != nil)
{
[[controlPanel contentView] addSubview:newPhaseView];
[newPhaseView setFrameOrigin:NSMakePoint (281, 8)];
currentPhaseView = newPhaseView;
// Show updated panel immediately.
//[newPhaseView display];
[newPhaseView setNeedsDisplay:YES];
[statusView setNeedsDisplay:YES];
//[controlPanel display]; // And this in turn will redisplay the status view.
}
else
{
[self _logGameState];
}
if (activePlayerCount > 1)
{
// Prevent deep recursion:
[self performSelector:@selector (executeCurrentPhase:) withObject:self afterDelay:0];
}
else
{
//NSLog (@"The game should be over...");
}
}
//----------------------------------------------------------------------
- (void) executeCurrentPhase:sender
{
//NSLog (@"active player count: %d", activePlayerCount);
//[self _logGameState];
// Initiate next phase
switch (gameState)
{
case gs_choose_countries:
[players[currentPlayerNumber] chooseCountry];
break;
case gs_place_initial_armies:
[players[currentPlayerNumber] placeInitialArmies:armiesLeftToPlace];
break;
case gs_place_armies:
[self resetMovableArmiesForPlayerNumber:currentPlayerNumber];
//NSLog (@"current player: %d, count: %d", currentPlayerNumber, [[players[currentPlayerNumber] playerCards] count]);
if ([[players[currentPlayerNumber] playerCards] count] > 4)
{
[players[currentPlayerNumber] mustTurnInCards];
[self automaticallyTurnInCardsForPlayerNumber:currentPlayerNumber];
}
else
{
[players[currentPlayerNumber] mayTurnInCards];
}
[players[currentPlayerNumber] placeArmies:armiesLeftToPlace];
break;
case gs_attack:
[players[currentPlayerNumber] attackPhase];
break;
case gs_move_attacking_armies:
//NSLog (@"player #%d", currentPlayerNumber);
[players[currentPlayerNumber] moveAttackingArmies:armiesLeftToPlace
between:[armyPlacementValidator sourceCountry]:[armyPlacementValidator destinationCountry]];
break;
case gs_fortify:
[players[currentPlayerNumber] fortifyPhase:[configuration fortifyRule]];
break;
case gs_place_fortifying_armies:
//NSLog (@"Fortifying %d armies from: %@", armiesLeftToPlace, sourceCountry);
[players[currentPlayerNumber] placeFortifyingArmies:armiesLeftToPlace
fromCountry:[armyPlacementValidator sourceCountry]];
break;
default:
NSLog (@"Invalid game state.");
}
}
//----------------------------------------------------------------------
// Advance to next active player (regardless of current phase)
// Returns whether it wrapped.
//----------------------------------------------------------------------
- (BOOL) nextActivePlayer
{
BOOL wrappedFlag;
int limit;
//int l;
wrappedFlag = NO;
limit = MAX_PLAYERS;
// 0 1 2 3 4 5 6
#if 0
for (l = 0; l < MAX_PLAYERS; l++)
{
NSLog (@"[%d]: %@ %@", l, (playersActive[l] == YES) ? @"Y" : @"N", players[l]);
}
NSLog (@"currentPlayerNumber: %d", currentPlayerNumber);
#endif
do
{
currentPlayerNumber = (currentPlayerNumber + 1) % MAX_PLAYERS;
if (currentPlayerNumber == 0)
wrappedFlag = YES;
}
while (--limit > 0 && playersActive[currentPlayerNumber] == NO);
//NSLog (@"limit: %d", limit);
NSAssert (limit != 0, @"No active players.");
playerHasConqueredCountry = NO;
return wrappedFlag;
}
//----------------------------------------------------------------------
- (void) fortify:sender
{
// Fortify action should only be executed during the attack phase.
AssertGameState (gs_attack);
[self endTurn];
}
//----------------------------------------------------------------------
// End turn for interactive player. May skip over fortify phase.
- (void) endTurn:sender
{
// End turn action should only be executed in either the attack or fortify phase.
AssertGameState2 (gs_attack, gs_fortify);
[self endTurn];
if (gameState == gs_fortify)
[self endTurn];
}
//----------------------------------------------------------------------
- (void) moveAttackingArmies:(int)minimum between:(Country *)source:(Country *)destination
{
BOOL isInteractivePlayer;
NSView *newPhaseView;
int count;
AssertGameState (gs_attack);
// Allow the movement of the remaining armies into either source or destination.
[destination setTroopCount:minimum];
count = [source troopCount] - minimum;
//NSLog (@"minimum: %d, armiesLeftToPlace: %d", minimum, count);
gameState = gs_move_attacking_armies;
// What if armiesLeftToPlace == 0?
[armyPlacementValidator placeInEitherCountry:source orCountry:destination forPlayerNumber:currentPlayerNumber];
[source setTroopCount:0];
[mapView drawCountry:source];
if (currentPhaseView != nil)
{
[currentPhaseView removeFromSuperview];
currentPhaseView = nil;
}
isInteractivePlayer = [players[currentPlayerNumber] isInteractive];
newPhaseView = (isInteractivePlayer == YES) ? phasePlaceArmies : phaseComputerMove;
[self updatePhaseBox];
// From above:
[initialArmiesLeftTextField setStringValue:@"--"];
[self setArmiesLeftToPlace:count];
// You can always review your cards, even if you have none.
if ([players[currentPlayerNumber] canTurnInCardSet] == YES)
[turnInCardsButton setEnabled:YES];
else
[turnInCardsButton setEnabled:NO];
[[controlPanel contentView] addSubview:newPhaseView];
[newPhaseView setFrameOrigin:NSMakePoint (281, 8)];
currentPhaseView = newPhaseView;
[newPhaseView setNeedsDisplay:YES];
// Break the recursion:
[self performSelector:@selector (executeCurrentPhase:) withObject:self afterDelay:0];
}
//----------------------------------------------------------------------
- (void) fortifyArmiesFrom:(Country *)source
{
BOOL isInteractivePlayer;
NSView *newPhaseView;
FortifyRule fortifyRule;
int count;
//NSLog (@"source: %@", source);
AssertGameState (gs_fortify);
count = [source movableTroopCount];
if (count < 1)
return;
gameState = gs_place_fortifying_armies;
// Need to base this on current fortify rule
fortifyRule = [configuration fortifyRule];
switch (fortifyRule)
{
case OneToOneNeighbor:
//NSLog (@"1:1");
[armyPlacementValidator placeInOneNeighborOfCountry:source forPlayerNumber:currentPlayerNumber];
break;
case OneToManyNeighbors:
//NSLog (@"1:N");
[armyPlacementValidator placeInAnyNeighborOfCountry:source forPlayerNumber:currentPlayerNumber];
break;
case ManyToManyNeighbors:
//NSLog (@"N:M");
[armyPlacementValidator placeInAnyNeighborOfCountry:source forPlayerNumber:currentPlayerNumber];
break;
case ManyToManyConnected:
//NSLog (@"N:M*");
[armyPlacementValidator placeInConnectedCountries:source forPlayerNumber:currentPlayerNumber];
break;
default:
[armyPlacementValidator placeInOneNeighborOfCountry:source forPlayerNumber:currentPlayerNumber];
NSLog (@"Unknown fortify rule: %d", fortifyRule);
}
[source setTroopCount:[source unmovableTroopCount]];
[mapView drawCountry:source];
if (currentPhaseView != nil)
{
[currentPhaseView removeFromSuperview];
currentPhaseView = nil;
}
isInteractivePlayer = [players[currentPlayerNumber] isInteractive];
newPhaseView = (isInteractivePlayer == YES) ? phasePlaceArmies : phaseComputerMove;
[self updatePhaseBox];
// From above:
[initialArmiesLeftTextField setStringValue:@"--"];
[self setArmiesLeftToPlace:count];
[turnInCardsButton setEnabled:NO];
[[controlPanel contentView] addSubview:newPhaseView];
[newPhaseView setFrameOrigin:NSMakePoint (281, 8)];
currentPhaseView = newPhaseView;
[newPhaseView setNeedsDisplay:YES];
// Prevent deep recursion:
[self performSelector:@selector (executeCurrentPhase:) withObject:self afterDelay:0];
}
//----------------------------------------------------------------------
- (void) forceCurrentPlayerToTurnInCards
{
BOOL isInteractivePlayer;
NSView *newPhaseView;
AssertGameState (gs_attack);
isInteractivePlayer = [players[currentPlayerNumber] isInteractive];
gameState = gs_place_armies;
[self setArmiesLeftToPlace:0];
[mapView selectCountry:nil];
newPhaseView = (isInteractivePlayer == YES) ? phasePlaceArmies : phaseComputerMove;
[initialArmiesLeftTextField setStringValue:@"--"];
[armyPlacementValidator placeInAnyCountryForPlayerNumber:currentPlayerNumber];
[turnInCardsButton setEnabled:YES];
[[controlPanel contentView] addSubview:newPhaseView];
[newPhaseView setFrameOrigin:NSMakePoint (281, 8)];
currentPhaseView = newPhaseView;
// Show updated panel immediately.
[newPhaseView setNeedsDisplay:YES];
[statusView setNeedsDisplay:YES];
// Prevent deep recursion:
[self performSelector:@selector (executeCurrentPhase:) withObject:self afterDelay:0];
}
//----------------------------------------------------------------------
- (void) resetMovableArmiesForPlayerNumber:(Player)number
{
NSEnumerator *countryEnumerator;
Country *country;
countryEnumerator = [[world countriesForPlayer:number] objectEnumerator];
while (country = [countryEnumerator nextObject])
{
[country resetUnmovableTroops];
}
}
//======================================================================
// Choose countries
//======================================================================
- (BOOL) player:(RiskPlayer *)aPlayer choseCountry:(Country *)country
{
Player number;
BOOL valid;
AssertGameState (gs_choose_countries);
number = [aPlayer playerNumber];
NSAssert (currentPlayerNumber == number, @"Not your turn.");
valid = NO;
if ([country playerNumber] == 0)
{
[country setPlayerNumber:number];
valid = YES;
}
return valid;
}
//----------------------------------------------------------------------
// unoccupied means playerNumber == 0 (not troopCounty == 0)
- (NSArray *) unoccupiedCountries
{
NSMutableArray *array;
NSEnumerator *countryEnumerator;
Country *country;
array = [NSMutableArray array];
countryEnumerator = [[world allCountries] objectEnumerator];
while (country = [countryEnumerator nextObject])
{
if ([country playerNumber] == 0)
[array addObject:country];
}
return array;
}
//----------------------------------------------------------------------
- (void) randomlyChooseCountriesForActivePlayers
{
NSMutableArray *array;
Country *country;
int count, index;
AssertGameState (gs_choose_countries);
currentPlayerNumber = 0;
array = [NSMutableArray arrayWithArray:[[world allCountries] allObjects]];
count = [array count];
while (count > 0)
{
[self nextActivePlayer];
index = [rng randomNumberModulo:count];
country = [array objectAtIndex:index];
[country setPlayerNumber:currentPlayerNumber];
[array removeObjectAtIndex:index];
count--;
}
}
//======================================================================
// Place Armies
//======================================================================
- (BOOL) player:(RiskPlayer *)aPlayer placesArmies:(int)count inCountry:(Country *)country
{
BOOL okay;
AssertGameState4 (gs_place_armies, gs_place_initial_armies, gs_move_attacking_armies, gs_place_fortifying_armies);
NSAssert2 (count <= armiesLeftToPlace, @"Tried to place too many(%d) armies. max: %d ", count, armiesLeftToPlace);
okay = [armyPlacementValidator validatePlacement:country];
if (okay == YES)
{
[armyPlacementValidator placeArmies:count inCountry:country];
if (gameState == gs_place_fortifying_armies)
{
[country addUnmovableTroopCount:count];
}
armiesLeftToPlace -= count;
if (gameState == gs_place_initial_armies)
[initialArmiesLeftTextField setIntValue:[initialArmiesLeftTextField intValue] - count];
[self setArmiesLeftToPlace:armiesLeftToPlace];
}
[country update];
return okay;
}
//======================================================================
// Attacking
//======================================================================
- (AttackResult) attackUntilUnableToContinueFromCountry:(Country *)attacker
toCountry:(Country *)defender
moveAllArmiesUponVictory:(BOOL)moveFlag
{
AttackResult attackResult;
attackResult.conqueredCountry = NO;
while (attackResult.conqueredCountry == NO && [attacker troopCount] > 0)
{
attackResult = [self attackOnceFromCountry:attacker toCountry:defender moveAllArmiesUponVictory:moveFlag];
}
return attackResult;
}
//----------------------------------------------------------------------
- (AttackResult) attackMultipleTimes:(int)count
fromCountry:(Country *)attacker
toCountry:(Country *)defender
moveAllArmiesUponVictory:(BOOL)moveFlag
{
AttackResult attackResult;
attackResult.conqueredCountry = NO;
while (count-- > 0 && attackResult.conqueredCountry == NO && [attacker troopCount] > 0)
{
attackResult = [self attackOnceFromCountry:attacker toCountry:defender moveAllArmiesUponVictory:moveFlag];
}
return attackResult;
}
//----------------------------------------------------------------------
- (AttackResult) attackFromCountry:(Country *)attacker
toCountry:(Country *)defender
untilArmiesRemain:(int)count
moveAllArmiesUponVictory:(BOOL)moveFlag
{
AttackResult attackResult;
attackResult.conqueredCountry = NO;
count = MAX (count, 0);
while (attackResult.conqueredCountry == NO && [attacker troopCount] > count)
{
attackResult = [self attackOnceFromCountry:attacker toCountry:defender moveAllArmiesUponVictory:moveFlag];
}
return attackResult;
}
//----------------------------------------------------------------------
// Specify move flag to avoid distracting switch of phase view for
// interactive players.
//
// When moveFlag == YES, we don't re-enter the attack phase, and
// therefore are not forced to turn in card sets when we have > 4
// cards.
//----------------------------------------------------------------------
//----------------------------------------------------------------------
// 1. Figure out how many dice to roll.
// 2. Get numbers for the dice. The arrays come back sorted.
// 3. Show the dice if needed.
// 4. Figure out how many countries each side loses.
// - Notify the computer player that it has been attacked so that
// it can retaliate later.
// 5. Now see if the defender lost.
// - If the country is conquered, notify the computer player that
// it has lost a country so that it can retaliate later.
//----------------------------------------------------------------------
- (AttackResult) attackOnceFromCountry:(Country *)attacker
toCountry:(Country *)defender
moveAllArmiesUponVictory:(BOOL)moveFlag
{
AttackResult attackResult;
DiceRoll diceRoll;
int compareCount;
int l;
int attackerLosses, defenderLosses;
Player attackingPlayerNumber, defendingPlayerNumber;
GameState initialGameState;
BOOL isGameOver;
NSAssert ([defender isAdjacentToCountry:attacker] == YES, @"The countries are not neighbors.");
initialGameState = gameState;
attackResult.conqueredCountry = NO;
isGameOver = NO;
attackingPlayerNumber = [attacker playerNumber];
defendingPlayerNumber = [defender playerNumber];
diceRoll = [self rollDiceWithAttackerArmies:[attacker troopCount] defenderArmies:[defender troopCount]];
// return YES if we won -- enter gs_move_attacking_armies state
if (diceInspector != nil && [diceInspector isPanelOnScreen] == YES)
{
[diceInspector showAttackFromCountry:attacker
toCountry:defender
withDice:diceRoll];
}
compareCount = MIN (diceRoll.attackerDieCount, diceRoll.defenderDieCount);
attackerLosses = 0;
defenderLosses = 0;
for (l = 0; l < compareCount; l++)
{
if (diceRoll.attackerDice[l] > diceRoll.defenderDice[l])
defenderLosses++;
else
attackerLosses++;
}
if (attackerLosses > 0)
{
[attacker addTroops:-attackerLosses];
[mapView drawCountry:attacker];
}
[players[defendingPlayerNumber] playerNumber:attackingPlayerNumber attackedCountry:defender];
if (defenderLosses > 0)
{
[defender addTroops:-defenderLosses];
[mapView drawCountry:defender];
// Still have to work out precise details... Old version always has 1 army in each country.
// How do we adjust for same effect, since we allow countries with troopCount == 0?
// Defender doesn't lose until troopCount < 0...
if ([defender troopCount] < 0)
{
[players[defendingPlayerNumber] playerNumber:attackingPlayerNumber capturedCountry:defender];
[defender setPlayerNumber:attackingPlayerNumber];
[defender setTroopCount:0];
[mapView drawCountry:defender];
attackResult.conqueredCountry = YES;
// Now, check to see if that was the last country of the defender.
if ([[world countriesForPlayer:defendingPlayerNumber] count] == 0)
{
[self transferCardsFromPlayer:players[defendingPlayerNumber] toPlayer:players[attackingPlayerNumber]];
isGameOver = [self checkForEndOfPlayerNumber:defendingPlayerNumber];
// And, check to see if activePlayerCount == 1
// Perhaps delay until after armies moved...
}
if (isGameOver == NO)
{
if (moveFlag == YES)
{
[defender setTroopCount:[attacker troopCount]];
[attacker setTroopCount:0];
[mapView drawCountry:attacker]; // May no longer need with removal of ARMY class...
[mapView drawCountry:defender];
if ([[players[attackingPlayerNumber] playerCards] count] > 4)
{
// And make sure we're back in the place armies phase...
[self forceCurrentPlayerToTurnInCards];
}
}
else
{
[self moveAttackingArmies:diceRoll.attackerDieCount between:attacker:defender];
}
}
playerHasConqueredCountry = YES;
}
}
// If the phase has changed, the player should return immediately. If it has not changed,
// the player may end the turn to enter the fortify phase.
attackResult.phaseChanged = initialGameState != gameState;
return attackResult;
}
//======================================================================
// Game Manager calculations
//======================================================================
- (int) earnedArmyCountForPlayer:(Player)number
{
int count;
// Calculated the number of armies earned at the beginning of a turn,
// based on the number of countries/continents controller by that
// player.
count = ([[world countriesForPlayer:number] count] / 3) + [world continentBonusArmiesForPlayer:number];
if (count < 3)
count = 3;
return count;
}
//----------------------------------------------------------------------
// Roll dice based on number of attacking/defending armies. Figures out
// proper number of dice to use, and returns sorted results.
//----------------------------------------------------------------------
- (DiceRoll) rollDiceWithAttackerArmies:(int)attackerArmies defenderArmies:(int)defenderArmies
{
DiceRoll diceRoll;
int l;
int temp1, temp2, temp3;
NSAssert (attackerArmies >= 0, @"Attacker army count must be positive.");
NSAssert (defenderArmies >= 0, @"Defender army count must be positive.");
temp1 = temp2 = temp3 = 0;
// Calculate number of dice to use for attacker and defender
diceRoll.attackerDieCount = (attackerArmies > 3) ? 3 : attackerArmies;
diceRoll.defenderDieCount = (defenderArmies == 0) ? 1 : 2;
// Roll dice and fill unused dice with 0 so that we can sort them.
for (l = 0; l < diceRoll.attackerDieCount; l++)
diceRoll.attackerDice[l] = [rng rollDieWithSides:6];
for (l = diceRoll.attackerDieCount; l < 3; l++)
diceRoll.attackerDice[l] = 0;
for (l = 0; l < diceRoll.defenderDieCount; l++)
diceRoll.defenderDice[l] = [rng rollDieWithSides:6];
for (l = diceRoll.defenderDieCount; l < 2; l++)
diceRoll.defenderDice[l] = 0;
// sort the arrays
if ((diceRoll.attackerDice[0] >= diceRoll.attackerDice[1]) && (diceRoll.attackerDice[0] >= diceRoll.attackerDice[2]))
{
temp1 = diceRoll.attackerDice[0];
if (diceRoll.attackerDice[1] >= diceRoll.attackerDice[2])
{
temp2 = diceRoll.attackerDice[1];
temp3 = diceRoll.attackerDice[2];
}
else
{
temp2 = diceRoll.attackerDice[2];
temp3 = diceRoll.attackerDice[1];
}
}
else if ((diceRoll.attackerDice[1] >= diceRoll.attackerDice[0]) && (diceRoll.attackerDice[1] >= diceRoll.attackerDice[2]))
{
temp1 = diceRoll.attackerDice[1];
if (diceRoll.attackerDice[0] >= diceRoll.attackerDice[2])
{
temp2 = diceRoll.attackerDice[0];
temp3 = diceRoll.attackerDice[2];
}
else
{
temp2 = diceRoll.attackerDice[2];
temp3 = diceRoll.attackerDice[0];
}
}
else if ((diceRoll.attackerDice[2] >= diceRoll.attackerDice[0]) && (diceRoll.attackerDice[2] >= diceRoll.attackerDice[1]))
{
temp1 = diceRoll.attackerDice[2];
if (diceRoll.attackerDice[0] >= diceRoll.attackerDice[1])
{
temp2 = diceRoll.attackerDice[0];
temp3 = diceRoll.attackerDice[1];
}
else
{
temp2 = diceRoll.attackerDice[1];
temp3 = diceRoll.attackerDice[0];
}
}
diceRoll.attackerDice[0] = temp1;
diceRoll.attackerDice[1] = temp2;
diceRoll.attackerDice[2] = temp3;
if (diceRoll.defenderDice[1] > diceRoll.defenderDice[0])
{
temp1 = diceRoll.defenderDice[1];
diceRoll.defenderDice[1] = diceRoll.defenderDice[0];
diceRoll.defenderDice[0] = temp1;
}
return diceRoll;
}
//======================================================================
// General player interaction
//======================================================================
- (void) selectCountry:(Country *)aCountry
{
[mapView selectCountry:aCountry];
}
//----------------------------------------------------------------------
- (void) takeAttackMethodFromPlayerNumber:(Player)number
{
AttackMethod attackMethod;
int attackMethodValue;
int index;
attackMethod = [players[number] attackMethod];
attackMethodValue = [players[number] attackMethodValue];
switch (attackMethod)
{
case AttackMultipleTimes:
index = 1;
break;
case AttackUntilArmiesRemain:
index = 2;
break;
case AttackUntilUnableToContinue:
index = 3;
break;
case AttackOnce:
default:
index = 0;
break;
}
[attackMethodPopup selectItemAtIndex:index];
[methodCountSlider setIntValue:attackMethodValue];
}
//----------------------------------------------------------------------
- (void) setAttackMethodForPlayerNumber:(Player)number
{
int index;
AttackMethod attackMethods[] = { AttackOnce, AttackMultipleTimes, AttackUntilArmiesRemain, AttackUntilUnableToContinue };
index = [attackMethodPopup indexOfSelectedItem];
if (index < 0 || index > 3)
index = 0;
[players[number] setAttackMethod:attackMethods[index]];
[players[number] setAttackMethodValue:[methodCountSlider intValue]];
}
//----------------------------------------------------------------------
// Can't add ...Field, because, of course, then it will be used to set
// the connection.
- (void) setAttackingFromCountryName:(NSString *)string
{
[attackingFromTextField setStringValue:string];
}
//----------------------------------------------------------------------
- (void) attackMethodAction:sender
{
if (sender == methodCountSlider)
{
[methodCountTextField takeIntValueFrom:sender];
}
else if (sender == methodCountTextField)
{
[methodCountSlider takeIntValueFrom:sender];
}
[self setAttackMethodForPlayerNumber:currentPlayerNumber];
}
//----------------------------------------------------------------------
- (void) setArmiesLeftToPlace:(int)count
{
armiesLeftToPlace = count;
[armyView setArmyCount:armiesLeftToPlace];
[armiesLeftToPlaceTextField setIntValue:armiesLeftToPlace];
}
//======================================================================
// Card management
//======================================================================
- (void) _recycleDiscardedCards
{
[cardDeck addObjectsFromArray:discardDeck];
[discardDeck removeAllObjects];
}
//----------------------------------------------------------------------
// We don't shuffle the deck -- instead, we just choose a random card.
// This is fine as long as we don't want a deck inspector for debugging.
// It may also be an issue for saved games -- loading the same saved
// game multiple times will result in different ordering of the cards.
//----------------------------------------------------------------------
- (void) dealCardToPlayerNumber:(Player)number
{
RiskCard *card;
int index, count;
count = [cardDeck count];
if (count == 0)
{
[self _recycleDiscardedCards];
count = [cardDeck count];
}
if (count == 0)
return;
//NSAssert (count != 0, @"Ran out of cards!");
index = [rng randomNumberModulo:count];
card = [cardDeck objectAtIndex:index];
[players[number] addCardToHand:card];
[cardDeck removeObjectAtIndex:index];
[statusView setNeedsDisplay:YES];
}
//----------------------------------------------------------------------
- (int) _valueOfNextCardSet:(int)currentValue
{
CardSetRedemption cardSetRedemption;
int nextValue;
cardSetRedemption = [configuration cardSetRedemption];
switch (cardSetRedemption)
{
case IncreaseByOne:
nextValue = currentValue + 1;
break;
case IncreaseByFive:
if (currentValue < 12)
nextValue = currentValue + 2;
else if (currentValue == 12)
nextValue = 15;
else
nextValue = currentValue + 5;
break;
case RemainConstant:
default:
nextValue = 5;
}
return nextValue;
}
//----------------------------------------------------------------------
- (int) armiesForNextCardSet
{
return nextCardSetValue;
}
//----------------------------------------------------------------------
// The player is *NOT* responsible for removing cards from hand.
// How are computer players affected by the forced turning in of cards?
// i.e. There is placeArmies: followed by optionally turning in card sets
// and then there is the forced turning in of card sets, followed by placeArmies:
//----------------------------------------------------------------------
- (void) turnInCardSet:(CardSet *)cardSet forPlayerNumber:(Player)number
{
RiskCard *card;
Country *country;
AssertGameState (gs_place_armies);
// Add number of armies to currently available armies for placement.
// Add bonus armies to those card countries that we control.
if (cardSet != nil)
{
//NSLog (@"turning in this card set: %@", cardSet);
card = [cardSet card1];
country = [card country];
if ([country playerNumber] == number)
{
[country addTroops:2];
[mapView drawCountry:country];
}
[players[number] removeCardFromHand:card];
[discardDeck addObject:card];
card = [cardSet card2];
country = [card country];
if ([country playerNumber] == number)
{
[country addTroops:2];
[mapView drawCountry:country];
}
[players[number] removeCardFromHand:card];
[discardDeck addObject:card];
card = [cardSet card3];
country = [card country];
if ([country playerNumber] == number)
{
[country addTroops:2];
[mapView drawCountry:country];
}
[players[number] removeCardFromHand:card];
[discardDeck addObject:card];
[self setArmiesLeftToPlace:armiesLeftToPlace + nextCardSetValue];
[players[number] didTurnInCards:nextCardSetValue];
//armiesLeftToPlace += nextCardSetValue;
nextCardSetValue = [self _valueOfNextCardSet:nextCardSetValue];
[statusView setNeedsDisplay:YES];
}
}
//----------------------------------------------------------------------
// For the currently active (interactive) player
//----------------------------------------------------------------------
- (void) reviewCards:sender
{
[self _loadCardPanel];
[cardPanelController runCardPanel:NO forPlayer:players[currentPlayerNumber]];
}
//----------------------------------------------------------------------
- (void) turnInCards:sender
{
AssertGameState (gs_place_armies);
[self _loadCardPanel];
[cardPanelController runCardPanel:YES forPlayer:players[currentPlayerNumber]];
if ([players[currentPlayerNumber] canTurnInCardSet] == YES)
{
[turnInCardsButton setEnabled:YES];
}
else
{
[turnInCardsButton setEnabled:NO];
}
}
//----------------------------------------------------------------------
- (void) automaticallyTurnInCardsForPlayerNumber:(Player)number
{
CardSet *cardSet;
while ([[players[number] playerCards] count] > 4)
{
cardSet = [players[number] bestSet];
[self turnInCardSet:cardSet forPlayerNumber:number];
}
}
//----------------------------------------------------------------------
- (void) transferCardsFromPlayer:(RiskPlayer *)source toPlayer:(RiskPlayer *)destination
{
NSArray *cardArray;
NSEnumerator *cardEnumerator;
RiskCard *card;
cardArray = [NSArray arrayWithArray:[source playerCards]];
cardEnumerator = [cardArray objectEnumerator];
//NSLog (@"transfering %d cards.", [cardArray count]);
while (card = [cardEnumerator nextObject])
{
[source removeCardFromHand:card];
[destination addCardToHand:card];
}
[statusView setNeedsDisplay:YES];
}
//----------------------------------------------------------------------
- (void) _loadCardPanel
{
if (cardPanelController == nil)
{
cardPanelController = [[CardPanelController alloc] init];
[cardPanelController setGameManager:self];
}
}
//----------------------------------------------------------------------
- (void) updatePhaseBox
{
if ([players[currentPlayerNumber] isInteractive] == YES)
{
[infoTextField setStringValue:gameStateInfo (gameState)];
[phaseTextField setStringValue:NSStringFromGameState (gameState)];
}
else
{
[phaseTextField setStringValue:@"Computer Move"];
[infoTextField setStringValue:@"The computer player named above is moving, please wait."];
}
}
//----------------------------------------------------------------------
- (int) totalTroopsForPlayerNumber:(Player)number
{
NSEnumerator *countryEnumerator;
Country *country;
int total;
total = 0;
countryEnumerator = [[world countriesForPlayer:number] objectEnumerator];
while (country = [countryEnumerator nextObject])
{
total += [country troopCount];
}
return total;
}
//----------------------------------------------------------------------
- (void) defaultsChanged:(NSNotification *)aNotification
{
if (currentPlayerNumber > 0)
[playerColorWell setColor:[[BoardSetup instance] colorForPlayer:currentPlayerNumber]];
}
//======================================================================
// End of game stuff:
//======================================================================
- (BOOL) checkForEndOfPlayerNumber:(Player)number
{
BOOL isGameOver;
Player winner;
isGameOver = NO;
if ([[world countriesForPlayer:number] count] == 0)
{
[self playerHasLost:number];
if (activePlayerCount < 2)
{
isGameOver = YES;
}
}
if (isGameOver == YES)
{
for (winner = 1; winner < MAX_PLAYERS; winner++)
{
if (playersActive[winner] == YES)
{
// Use notification instead, and the brain can do the alert panel.
// And make gameState == gs_game_over...
NSRunAlertPanel (@"Victory", @"Winner was %@.", @"OK", nil, nil, [players[winner] playerName]);
[self playerHasWon:winner];
break;
}
}
[self stopGame];
}
return isGameOver;
}
//----------------------------------------------------------------------
- (void) playerHasLost:(Player)number
{
[players[number] youLostGame];
[self deactivatePlayerNumber:number];
}
//----------------------------------------------------------------------
- (void) playerHasWon:(Player)number
{
[players[number] youWonGame];
[self deactivatePlayerNumber:number];
}
//----------------------------------------------------------------------
// 1. Release player
// 2. Free items of the player's submenu (if any), and disable the player menu.
//----------------------------------------------------------------------
- (void) deactivatePlayerNumber:(Player)number
{
NSMenu *playerMenu;
NSArray *itemArray;
NSMenuItem *menuItem;
if (playersActive[number] == YES)
{
playersActive[number] = NO;
if (players[number] != nil)
{
playerMenu = [players[number] playerToolMenu];
itemArray = [playerMenu itemArray];
while ([itemArray count] > 1)
{
menuItem = [itemArray lastObject];
//NSLog (@"removing item: %@", [menuItem title]);
[playerMenu removeItem:menuItem];
}
// Best to autorelease, especially for the Human player.
[players[number] autorelease];
players[number] = nil;
}
activePlayerCount--;
}
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.