ftp.nice.ch/pub/next/games/board/Risk.0.97.s.tar.gz#/RiskSource0.97/Risk/Mover.m

This is Mover.m in view mode; [Download] [Up]

//  Mover.m
// Part of Risk by Mike Ferris

#import "Mover.h"
#import "GameSetup.h"
#import "ArmyView.h"
#import "MapView.h"
#import "Country.h"
#import "ComputerLogic.h"
#import "ComputerPlayer.h"
#import "DiceInspector.h"
#import <stdlib.h>
#import <strings.h>
#import <objc/List.h>
#import <appkit/Panel.h>
#import <appkit/View.h>
#import <appkit/TextField.h>
#import <appkit/NXColorWell.h>
#import <appkit/publicWraps.h>
#import <appkit/nextstd.h>
#import <appkit/Application.h>
#import "CardManager.h"

@implementation Mover

+ initialize
{
	if (self == [Mover class])  {
		[self setVersion:1];
	}
	return self;
}

- appDidInit:sender
{
	int i;
	
	diceInspector = nil;
	gameInProgress=NO;
	initPlaceArmiesNum=-1;
	fortifyRule = FR_NIL;
	
	currentPhaseView=nil;
	currentPhase=P_NIL;
	placeArmiesSubphase=SP_INITIAL;
	phaseResults=PR_NIL;

	currentPlayer=-1;
	currentTurn=0;
	currentPlayerGetsCard=NO;

	currentCountry=nil;
	attackFromCountry=nil;
	attackToCountry=nil;
	defaultAttackFrom=nil;

	for (i=0;i<6;i++)  {
		attackOption[i]=AP_ONCE;
		attackNum[i]=3;
		initPlaceArmies[i]=0;
	}
	
	[phaseComputerMove removeFromSuperview];
	[phasePlaceArmies removeFromSuperview];
	[phaseAttack removeFromSuperview];
	[phaseFortify removeFromSuperview];
	[phaseChooseCountries removeFromSuperview];
	
	[colorWell setEnabled:NO];
	[colorWell setColor:NX_COLORWHITE];
	[colorWell display];
	
	return self;
}

- startNewGame
{
	gameInProgress=YES;
	initPlaceArmiesNum=[theGameSetup initialArmyPlacement];
	fortifyRule = [theGameSetup fortifyRule];
	
	[theMapView makeAllCountriesUnoccupied];
	currentPhase=P_NIL; currentPlayer=-1;
	[theCardManager reset];
	[self continuePlay];
	
	return self;
}

- settingsChanged
{
	initPlaceArmiesNum=[theGameSetup initialArmyPlacement];
	fortifyRule = [theGameSetup fortifyRule];
	[theMapView display];
	[nameTextField setStringValue:[theGameSetup nameOfPlayer:currentPlayer]];
	[colorWell setColor:[theGameSetup colorOfPlayer:currentPlayer]];
	[colorWell display];
	[statusView display];
	return self;
}

- endGame:(int)winner
{
	NXRunAlertPanel("Victory", "Winner was %s.", "OK", NULL, NULL,
			[theGameSetup nameOfPlayer:winner]);
	gameInProgress=NO;
	currentCountry=nil;
	[theMapView setSelectedCountry:currentCountry];
	// flush the control panel view
	[self continuePlay];
	[controlPanel display];
	return self;
}

- countryClick:country num:(int)n event:(NXEvent *)e
// dispatch function for events where the player has clicked in a country.
// determines what phase of which players turn it is, and dispatches the
// event accordingly.
{
	if (gameInProgress)  {
		switch (currentPhase)  {
			case P_NIL:
				currentCountry=nil;
				[theMapView setSelectedCountry:currentCountry];
				break;
			case P_CHOOSECOUNTRIES:
				[self chooseCountry:country num:n];
				break;
			case P_PLACEARMIES:
				[self placeArmiesInCountry:country num:n event:e];
				break;
			case P_ATTACK:
				if ([country player]==currentPlayer)  {
					[self attackFrom:country num:n];
				}  else  {
					[self attackTo:country num:n event:e];
				}
				break;
			case P_FORTIFY:
				if ([country player]==currentPlayer)  {
					[self fortifyFrom:country num:n];
				}
				break;
			default:
				break;
		}
	}  else  {
		currentCountry=nil;
		[theMapView setSelectedCountry:currentCountry];
	}
	return self;
}

- placeArmiesInCountry:country num:(int)n event:(NXEvent *)e
{
	int armiesToPlace=1;
	BOOL doit=NO;
	
	if (e->flags & NX_SHIFTMASK)  {
		if ([placeArmyView armiesLeft]<5)  {
			armiesToPlace=[placeArmyView armiesLeft];
		}  else  {
			armiesToPlace=5;
		}
	}
	if (e->flags & NX_COMMANDMASK)  {
		armiesToPlace=[placeArmyView armiesLeft];
	}
	switch (placeArmiesSubphase)  {
		case SP_INITIAL:
		case SP_BEGINTURN:
			if ([country player] == currentPlayer)  {
				currentCountry=nil;
				defaultAttackFrom=country;
				doit=YES;
			}  else  {
				doit=NO;
			}
			break;
		case SP_ATTACK:
			if ((country==attackFromCountry) || (country==attackToCountry))  {
				doit=YES;
			}  else  {
				doit=NO;
			}
			break;
		case SP_FORTIFY:
			// make this canFortifyTo:country
			doit = [self canFortifyTo:country];
			break;
	}
	
	if (doit)  {
		[country addArmies:armiesToPlace];
		if (placeArmiesSubphase==SP_FORTIFY)  {
			[country addUnmovableArmies:armiesToPlace forTurn:currentTurn];
		}
		[placeArmyView decreaseArmiesLeftBy:armiesToPlace];
		if (initPlaceArmies[currentPlayer] > 0)  {
			[placeArmyInitTextField setIntValue:
					[placeArmyInitTextField intValue] - armiesToPlace];
		}
		[theMapView displayCountry:country];
		[theMapView setSelectedCountry:currentCountry];
		if ([placeArmyView armiesLeft]<=0)  {
			[self continuePlay];
		}
	}  else  {
		[theMapView setSelectedCountry:currentCountry];
		NXBeep();
	}
	
	return self;
}

- attackFrom:country num:(int)n
{
	// if the clicked country has enough armies to attack, let em.
	if ([country armies]>1)  {
		currentCountry = attackFromCountry = country;
		[attackCountry setStringValue:[currentCountry name]];
	}  else  {
		NXBeep();
	}
	[theMapView setSelectedCountry:currentCountry];
	return self;
}

- attackTo:country num:(int)n event:(NXEvent *)e
{
	BOOL conq, moveAll = NO;
	id fc = currentCountry;
	id tc = country;
	int aArmies = [fc armies];
	int aPlayer = [fc player];
	int dPlayer = [tc player];
	int oldAttackOption=attackOption[currentPlayer];
	
	if (e->flags & NX_SHIFTMASK)  {
		moveAll = YES;
	}
	if (e->flags & NX_COMMANDMASK)  {
		attackOption[currentPlayer]=AP_ONCE;
	}
	
	
	if (([country isNeighborTo:[currentCountry idNum]]) && 
			([country player] != currentPlayer))  {
		// do the attack from currentCountry to country
		attackToCountry=tc;
		conq = [self attackFrom:fc to:tc];
		if (conq)  {
			defaultAttackFrom=country;
			aArmies = [fc armies];
			[fc setPlayer:aPlayer andArmies:1];
			if (moveAll)  {
				[tc setPlayer:aPlayer andArmies:aArmies-1];
				[fc setPlayer:aPlayer andArmies:1];
				[placeArmyView setArmiesLeft:0];
			}  else  {
				if (aArmies > 3)  {
					[tc setPlayer:aPlayer andArmies:3];
					[placeArmyView setArmiesLeft:aArmies-4];
				}  else  {
					[tc setPlayer:aPlayer andArmies:aArmies-1];
					[placeArmyView setArmiesLeft:0];
				}
			}
			[theMapView displayCountry:fc];
			[theMapView displayCountry:tc];
			// now check to see if defending player still exists
			if (![theMapView playerOccupiesCountries:dPlayer])  {
				// dPlayer is out of game
				[theGameSetup playerConquered:dPlayer];
				if ([theMapView doesPlayerWin:aPlayer])  {
					[self endGame:aPlayer];
				}  else  {
					[theCardManager player:aPlayer takesCardsOf:dPlayer];
				}
			}
			if (gameInProgress)  {
				currentCountry = nil;
				[theMapView setSelectedCountry:currentCountry];
				phaseResults=PR_A_VICTORY;
				placeArmiesSubphase=SP_ATTACK;
				[self continuePlay];
			}
		}  else  {
			if ([fc armies]<=1)  {
				currentCountry=nil;
			}
		}
	}  else  {
		NXBeep();
	}
	attackOption[currentPlayer]=oldAttackOption;

	[theMapView setSelectedCountry:currentCountry];
	return self;
}

- (BOOL)attackFrom:fc to:tc
// calls attackOnceFrom: to: according to the attackOption setting
// it can call it just once or it can call it repeatedly until a certain
// condition is met.
{
	int i;
	BOOL conquered, cont;
	
	switch (attackOption[currentPlayer])  {
		case AP_ONCE:
			conquered = [self attackOnceFrom:fc to:tc];
			break;
		case AP_MULTIPLE:
			cont=YES;
			conquered=NO;
			for (i=[attackSlider intValue];(i>0) && (cont);i--)  {
				conquered=[self attackOnceFrom:fc to:tc];
				// if the battle was won don't keep going or
				// if the attacker can't attack anymore stop
				if ((conquered) || ([fc armies]<2))  {
					cont=NO;
				}
			}
			break;
		case AP_UNTILLEFT:
			cont=YES;
			conquered = NO;
			i=[attackSlider intValue];
			while (cont)  {
				conquered = [self attackOnceFrom:fc to:tc];
				if ((conquered) || ([fc armies]<=i))  {
					cont=NO;
				}
			}
			break;
		case AP_UNTILCANT:
			cont=YES;
			conquered = NO;
			while (cont)  {
				conquered = [self attackOnceFrom:fc to:tc];
				if ((conquered) || ([fc armies]<2))  {
					cont=NO;
				}
			}
			break;
		default:
			conquered = NO;
			break;
	}
	return conquered;
}

- (BOOL)attackOnceFrom:fc to:tc
// this method makes one attack from fc to tc.
// it subtracts the required armies from fc and tc and redisplays
// changing phase and setting the new current country are the caller's
// responsibility.
// returns whether fc conquered tc.
{
	int aDice[3], dDice[2];
	int aNumDice, dNumDice;
	int aArmies = [fc armies];
	int dArmies = [tc armies];
	int aLoses=0, dLoses=0;
	int i, numCompares;
	
	// first figure out how many dice to roll
	if (aArmies>3)  {
		aNumDice=3;
	}  else  {
		aNumDice=aArmies-1;
	}
	if (dArmies>1)  {
		dNumDice=2;
	}  else  {
		dNumDice=1;
	}
	numCompares = MIN(aNumDice, dNumDice);
	
	// now get numbers for the dice. the arrays come back sorted.
	[computerLogic rollDiceAttacker:aDice :aNumDice defender:dDice :dNumDice];
	
	// show the dice if needed
	if ((diceInspector != nil) && ([diceInspector panelOnScreen]))  {
		[diceInspector setFromCountry:[fc name] toCountry:[tc name]
				fromArmies:aArmies toArmies:dArmies attackerRolling:aNumDice
				attackerUsing:numCompares attackerDie1:aDice[0]
				attackerDie2:aDice[1] attackerDie3:aDice[2] 
				defenderRolling:dNumDice defenderUsing:numCompares
				attackerDie1:dDice[0] attackerDie2:dDice[1]];
	}
	// figure out how many countries each side loses
	for (i=0;i<numCompares;i++)  {
		if (aDice[i]>dDice[i])  {
			// attacker wins this roll
			dLoses++;
		}  else  {
			// defender wins this roll
			aLoses++;
		}
	}
	
	// aLoses and dLoses are set to the number of armies each is to lose
	// first get rid of attackers lost armies
	if (aLoses != 0)  {
		[fc subArmies:aLoses];
		[theMapView displayCountry:fc];
	}
	if (dLoses!=0)  {
		[tc subArmies:dLoses];
		[theMapView displayCountry:tc];
	}
	if ([theGameSetup strategyOfPlayer:[tc player]] == S_COMPUTER)  {
		// notify the computer player that it has been affronted so
		// it can retaliate later
		[[theGameSetup computerPlayerForPlayer:[tc player]] 
				youWereAttacked:tc by:[fc player]];
	}
		
	
	// now see if the defender lost
	if (dLoses >= dArmies)  {
		// country is conquered
		currentPlayerGetsCard=YES;
		if ([theGameSetup strategyOfPlayer:[tc player]] == S_COMPUTER)  {
			// notify the computer player that it has lost a country so
			// it can retaliate later
			[[theGameSetup computerPlayerForPlayer:[tc player]] 
					youLostCountry:tc to:[fc player]];
		}
		return YES;
	}  else  {
		return NO;
	}
}

- endTurn:sender
{
	if (gameInProgress)  {
		phaseResults=PR_ENDTURN;
		currentCountry=nil;
		defaultAttackFrom=nil;
		[theMapView setSelectedCountry:currentCountry];
		[self perform:@selector(continuePlay:) with:self afterDelay:5 
				cancelPrevious:YES];
	}
	return self;
}

- fortify:sender
{
	if (gameInProgress)  {
		phaseResults=PR_A_FORTIFY;
		currentTurn+=1;
		currentCountry=nil;
		[theMapView setSelectedCountry:currentCountry];
		[self perform:@selector(continuePlay:) with:self afterDelay:5 
				cancelPrevious:YES];
	}
	return self;
}

- fortifyFrom:country num:(int)n
{
	int numToPlace;
	
	numToPlace = [country movableArmiesForTurn:currentTurn];
	if (numToPlace > 0)  {
		currentCountry=country;
		attackFromCountry = country;
		attackToCountry=nil;
		phaseResults = PR_F_PLACE;
		placeArmiesSubphase = SP_FORTIFY;
		[country subArmies:numToPlace];
		[placeArmyView setArmiesLeft:numToPlace];
		[theMapView displayCountry:country];
		[theMapView setSelectedCountry:currentCountry];
		[self continuePlay];
	}
	
	return self;
}

- (BOOL)canFortifyTo:country
// this function returns yes if the player can fortify to the country
// in question from the attackFromCountry.  It also keeps track of
// attackToCountry so it can tell.
{
	// we can always fortify back into the country we're fortifying out of
	if (country == attackFromCountry)  {
		return YES;
	}
	// you can never fortify into somebody else's country
	if ([country player]!=currentPlayer)  {
		return NO;
	}
	
	switch(fortifyRule)  {
		case FR_ONE_ONE_N:
			// any neighbor, but just one
			if ((attackToCountry == nil) && 
					([country isNeighborTo:[attackFromCountry idNum]]))  {
				attackToCountry = country;
			}
			return (country == attackToCountry);
			break;
		case FR_ONE_MANY_N:
		case FR_MANY_MANY_N:
			// any neighbor is ok
			return [country isNeighborTo:[attackFromCountry idNum]];
			break;
		case FR_MANY_MANY_C:
			return [country isConnectedTo:[attackFromCountry idNum] 
					withMapList:[theMapView countryList] 
					alreadyTried:NULL];
			break;
		default:
			return NO;
	}
	return NO;
}

- chooseCountry:country num:(int)n
{
	if ([country player] == -1)  {
		[country setPlayer:currentPlayer andArmies:1];
		currentCountry=nil;
		[theMapView setSelectedCountry:currentCountry];
		[self continuePlay];
	}  else  {
		currentCountry=nil;
		[theMapView setSelectedCountry:currentCountry];
		NXBeep();
	}

	return self;
}

- continuePlay:sender
// cover for following method
{
	return [self continuePlay];
}

- continuePlay
	// upon entry to this function it is assumed that the caller has completed
	// the current phase.
	// specifically it is assumed that if the current phase is attack or 
	// fortify, that phaseResults and placeArmySubphase are set accurately, and 
	// if the results are either PR_A_VICTORY or PR_F_PLACE that placeArmyView
	// has been set to display the proper number of armies to be placed.
	// also upon entry currentCountry is expected to be
	// accurate.
{
	id theCP;
	int anum, cnum;
	
	[self goNextPhase];
	switch (currentPhase)  {
		case P_CHOOSECOUNTRIES:
			if ([theGameSetup countryDistribution] == CO_RANDOM)  {
				[computerLogic randomCountries:self];
				[theMapView display];
				[self goNextPhase];
			}
			break;
		default:
			break;
	}
	while (([theGameSetup strategyOfPlayer:currentPlayer] == S_COMPUTER) && 
			(gameInProgress))  {
		theCP = [theGameSetup computerPlayerForPlayer:currentPlayer];
		// it's a computerplayer
		switch (currentPhase)  {
			case P_CHOOSECOUNTRIES:
				[theCP yourChooseCountry];
				break;
			case P_PLACEARMIES:
				if (placeArmiesSubphase == SP_INITIAL)  {
					if (initPlaceArmiesNum > 
							initPlaceArmies[currentPlayer])  {
						[theCP yourInitialPlaceArmies:
								initPlaceArmies[currentPlayer]];
					}  else  {
						[theCP yourInitialPlaceArmies:initPlaceArmiesNum];
					}
				}  else if (placeArmiesSubphase == SP_BEGINTURN)  {
					anum = [theMapView armiesForPlayer:currentPlayer];
					cnum = [theCardManager numCardsForPlayer:currentPlayer];
					[theCP yourTurnWithArmies:anum andCards:cnum];
					currentPhase = P_FORTIFY;
					phaseResults = PR_ENDTURN;
				}
				[placeArmyView setArmiesLeft:0];
				break;
			default:
				break;
		}
		if (NXUserAborted())  {
			int result;
			// the user pressed command-. while we were in a computer
			// player.  Ask if we should abort the game.
			result=NXRunAlertPanel("Interrupt", "You pressed command-.  "
					"Do you wish to abort the game in progress?",
					"Yes", "No", NULL);
			if (result == NX_ALERTDEFAULT)  {
				gameInProgress = NO;
			}
			NXResetUserAbort();
		}
		[self goNextPhase];
	}
	return self;
}

- goNextPhase
	// this function should be called exclusively from continuePlay
{
	int i, pl=-1, ph=-1;
	
	switch (currentPhase)  {
		case P_NIL:
			pl=0;
			while ([theGameSetup strategyOfPlayer:pl] ==S_NOTPLAYING)  {
				pl=(pl+1)%6;
			}
			ph=P_CHOOSECOUNTRIES;
			break;
		case P_CHOOSECOUNTRIES:
			if ([theMapView countriesAreUnoccupied])  {
				ph=currentPhase;
				pl=(currentPlayer+1)%6;
				while ([theGameSetup strategyOfPlayer:pl] ==S_NOTPLAYING)  {
					pl=(pl+1)%6;
				}
			}  else  {
				pl=0;
				while ([theGameSetup strategyOfPlayer:pl] ==S_NOTPLAYING)  {
					pl=(pl+1)%6;
				}
				ph=P_PLACEARMIES;
				placeArmiesSubphase=SP_INITIAL;
				[placeArmyView setArmiesLeft:initPlaceArmiesNum];
				for (i=0;i<6;i++)  {
					initPlaceArmies[i] = [theGameSetup 
							initialPlaceArmiesForPlayer:i];
				}
				[placeArmyInitStatText setTextGray:NX_BLACK];
				[placeArmyInitTextField setTextGray:NX_BLACK];
				[placeArmyInitTextField setIntValue:initPlaceArmies[pl]];
			}
			break;
		case P_PLACEARMIES:
			switch (placeArmiesSubphase)  {
				case SP_INITIAL:
					if (initPlaceArmiesNum > initPlaceArmies[currentPlayer])  {
						initPlaceArmies[currentPlayer] -= 
								initPlaceArmies[currentPlayer];
					}  else  {
						initPlaceArmies[currentPlayer] -= initPlaceArmiesNum;
					}
					pl=(currentPlayer+1)%6;
					while ([theGameSetup strategyOfPlayer:pl] 
							==S_NOTPLAYING)  {
						pl=(pl+1)%6;
					}
					if (initPlaceArmies[pl] > 0)  {
						[placeArmyInitTextField 
								setIntValue:initPlaceArmies[pl]];
						ph=P_PLACEARMIES;
						if (initPlaceArmiesNum > 
								initPlaceArmies[pl])  {
							[placeArmyView 
								setArmiesLeft:initPlaceArmies[pl]];
						}  else  {
							[placeArmyView setArmiesLeft:initPlaceArmiesNum];
						}
					}  else  {
						[placeArmyInitStatText setTextGray:NX_DKGRAY];
						[placeArmyInitTextField setTextGray:NX_DKGRAY];
						[placeArmyInitTextField 
								setStringValue:"--"];
						ph=P_PLACEARMIES;
						placeArmiesSubphase=SP_BEGINTURN;
						[placeArmyView setArmiesLeft:
								[theMapView armiesForPlayer:pl]];
					}
					break;
				case SP_BEGINTURN:
					ph=P_ATTACK;
					pl=currentPlayer;
					if (defaultAttackFrom != nil)  {
						if ([defaultAttackFrom armies] > 1)  {
							[self attackFrom:defaultAttackFrom 
									num:[defaultAttackFrom idNum]];
						}
					}
					break;
				case SP_ATTACK:
					// it is possible that we vanquished an opponent, so
					// check how many cards we have
					pl=currentPlayer;
					if ([theCardManager numCardsForPlayer:pl] > 4)  {
						ph=P_PLACEARMIES;
						placeArmiesSubphase=SP_BEGINTURN;
					}  else  {
						ph=P_ATTACK;
						if (defaultAttackFrom != nil)  {
							if ([defaultAttackFrom armies] > 1)  {
								[self attackFrom:defaultAttackFrom 
										num:[defaultAttackFrom idNum]];
							}
						}
					}
					break;
				case SP_FORTIFY:
					currentCountry=nil;
					[theMapView setSelectedCountry:currentCountry];
					if ((fortifyRule == FR_ONE_ONE_N) || 
							(fortifyRule == FR_ONE_MANY_N))  {
						if (currentPlayerGetsCard)  {
							[theCardManager playerDrawsCard:currentPlayer];
						}
						currentPlayerGetsCard=NO;
						ph=P_PLACEARMIES;
						placeArmiesSubphase=SP_BEGINTURN;
						pl=(currentPlayer+1)%6;
						while ([theGameSetup strategyOfPlayer:pl]==
								S_NOTPLAYING)  {
							pl=(pl+1)%6;
						}
						[placeArmyView setArmiesLeft:[theMapView 
								armiesForPlayer:pl]];
					}  else  {
						ph=P_FORTIFY;
						pl=currentPlayer;
					}
					break;
			}
			break;
		case P_ATTACK:
			switch (phaseResults)  {
				case PR_A_VICTORY:
					// check to see if we have to turn in cards
					pl=currentPlayer;
					if (([theCardManager numCardsForPlayer:pl] > 4) && 
							([placeArmyView armiesLeft]==0))  {
						ph=P_PLACEARMIES;
						placeArmiesSubphase=SP_BEGINTURN;
					}  else  {
						if ([placeArmyView armiesLeft]>0)  {
							ph=P_PLACEARMIES;
							placeArmiesSubphase=SP_ATTACK;
						} else {
							ph=P_ATTACK;
							if (defaultAttackFrom != nil)  {
								if ([defaultAttackFrom armies] > 1)  {
									[self attackFrom:defaultAttackFrom 
											num:[defaultAttackFrom idNum]];
								}
							}
						}
					}
					break;
				case PR_A_FORTIFY:
					if (currentPlayerGetsCard)  {
						[theCardManager playerDrawsCard:currentPlayer];
					}
					currentPlayerGetsCard=NO;
					ph=P_FORTIFY;
					pl=currentPlayer;
					break;
				case PR_ENDTURN:
					if (currentPlayerGetsCard)  {
						[theCardManager playerDrawsCard:currentPlayer];
					}
					currentPlayerGetsCard=NO;
					ph=P_PLACEARMIES;
					placeArmiesSubphase=SP_BEGINTURN;
					pl=(currentPlayer+1)%6;
					while ([theGameSetup strategyOfPlayer:pl]==S_NOTPLAYING)  {
						pl=(pl+1)%6;
					}
					[placeArmyView setArmiesLeft:[theMapView 
							armiesForPlayer:pl]];
					break;
			}
			break;
		case P_FORTIFY:
			switch (phaseResults)  {
				case PR_F_PLACE:
					ph=P_PLACEARMIES;
					pl=currentPlayer;
					placeArmiesSubphase=SP_FORTIFY;
					break;
				case PR_ENDTURN:
					if (currentPlayerGetsCard)  {
						[theCardManager playerDrawsCard:currentPlayer];
					}
					currentPlayerGetsCard=NO;
					ph=P_PLACEARMIES;
					placeArmiesSubphase=SP_BEGINTURN;
					pl=(currentPlayer+1)%6;
					while ([theGameSetup strategyOfPlayer:pl]==S_NOTPLAYING)  {
						pl=(pl+1)%6;
					}
					[placeArmyView setArmiesLeft:[theMapView 
							armiesForPlayer:pl]];
					break;
			}
			break;
		default:
			break;
	}
	if (!gameInProgress)  ph=P_NIL;
	[self phaseChange:ph forPlayer:pl];
	[statusView display];
	return self;
}

- phaseChange:(int)phaseNum forPlayer:(int)playerNum
{
	id view=nil;
	int strat = [theGameSetup strategyOfPlayer:playerNum];
	id pHand = [theCardManager playersHand:playerNum];
	int cardArmies;
	
	if ((currentPlayer==playerNum) && (phaseNum==P_ATTACK))  {
		[attackTextField setIntValue:attackNum[playerNum]];
		[attackSlider setIntValue:attackNum[playerNum]];
		switch (attackOption[currentPlayer])  {
			case AP_ONCE:
				[attackPopupButton setTitle:APN_ONCE];
				[attackTextField setEnabled:NO];
				[attackSlider setEnabled:NO];
				break;
			case AP_MULTIPLE:
				[attackPopupButton setTitle:APN_MULTIPLE];
				[attackTextField setEnabled:YES];
				[attackSlider setEnabled:YES];
				break;
			case AP_UNTILLEFT:
				[attackPopupButton setTitle:APN_UNTILLEFT];
				[attackTextField setEnabled:YES];
				[attackSlider setEnabled:YES];
				break;
			case AP_UNTILCANT:
				[attackPopupButton setTitle:APN_UNTILCANT];
				[attackTextField setEnabled:NO];
				[attackSlider setEnabled:NO];
				break;
			default:
				break;
		}
	}

	currentPhase=phaseNum;
	currentPlayer=playerNum;

	[statusView display];
	[controlPanel disableDisplay];
	if (currentPhaseView!=nil)  {
		[currentPhaseView removeFromSuperview];
	}
	if (strat==S_COMPUTER)  {
		view=phaseComputerMove;
		[nameTextField setStringValue:
				[theGameSetup nameOfPlayer:playerNum]];
		[colorWell setColor:[theGameSetup colorOfPlayer:playerNum]];
		[colorWell display];
		[phaseTextField setStringValue:"Computer Move"];
		[infoTextField setStringValue:"The computer player named above is "
				"moving, please wait."];		
	}	else  {
		switch (phaseNum)  {
			case P_NIL:
				view=nil;
				[nameTextField setStringValue:""];
				[colorWell setColor:NX_COLORWHITE];
				[colorWell display];
				[phaseTextField setStringValue:""];
				[infoTextField setStringValue:""];
				break;
			case P_PLACEARMIES:
				view=phasePlaceArmies;
				[nameTextField setStringValue:
						[theGameSetup nameOfPlayer:playerNum]];
				[colorWell setColor:[theGameSetup colorOfPlayer:playerNum]];
				[colorWell display];
				[phaseTextField setStringValue:"Place Armies"];
				switch (placeArmiesSubphase)  {
					case SP_INITIAL:
						[placeArmyTurnInButton setEnabled:NO];
						[placeArmyReviewButton setEnabled:NO];
						[infoTextField setStringValue:"The game begins by " 
							"players taking turns placing their initial "
							"armies a few at a time."];
						break;
					case SP_BEGINTURN:
						if (([pHand count]>4) && (strat!=S_COMPUTER))  {
							cardArmies = [theCardManager runCardPanel:YES 
									forPlayer:currentPlayer];
							[placeArmyView increaseArmiesLeftBy:cardArmies];
						}
						if ([theCardManager canPlayerPlay:currentPlayer])  {
							[placeArmyTurnInButton setEnabled:YES];
						}  else  {
							[placeArmyTurnInButton setEnabled:NO];
						}
						if ([pHand count] > 0)  {
							[placeArmyReviewButton setEnabled:YES];
						}  else  {
							[placeArmyReviewButton setEnabled:NO];
						}
						[infoTextField setStringValue:"Begin your turn by " 
							"placing new armies and possibly turning in "
							"cards."];
						break;
					case SP_ATTACK:
						[placeArmyTurnInButton setEnabled:NO];
						if ([pHand count] > 0)  {
							[placeArmyReviewButton setEnabled:YES];
						}  else  {
							[placeArmyReviewButton setEnabled:NO];
						}
						[infoTextField setStringValue:"You have conquered " 
							"a country.  Now place the available armies"
							"in either the attacking or the conquered "
							"countries."];
						break;
					case SP_FORTIFY:
						[placeArmyTurnInButton setEnabled:NO];
						if ([pHand count] > 0)  {
							[placeArmyReviewButton setEnabled:YES];
						}  else  {
							[placeArmyReviewButton setEnabled:NO];
						}
						[infoTextField setStringValue:"Fortify the armies " 
							"into the source country or any neighboring "
							"countries you control."];
						break;
					default:
						break;
				}
				break;
			case P_ATTACK:
				view=phaseAttack;
				[nameTextField setStringValue:
						[theGameSetup nameOfPlayer:playerNum]];
				[colorWell setColor:[theGameSetup colorOfPlayer:playerNum]];
				[colorWell display];
				[phaseTextField setStringValue:"Attack"];
				[infoTextField setStringValue:"Attack opponent's countries "
						"which border on your own countries."];
				if ([pHand count] > 0)  {
					[attackReviewButton setEnabled:YES];
				}  else  {
					[attackReviewButton setEnabled:NO];
				}
				break;
			case P_FORTIFY:
				view=phaseFortify;
				[nameTextField setStringValue:
						[theGameSetup nameOfPlayer:playerNum]];
				[colorWell setColor:[theGameSetup colorOfPlayer:playerNum]];
				[colorWell display];
				[phaseTextField setStringValue:"Fortify Position"];
				[infoTextField setStringValue:"Fortify your position at the "
						"end of your move by shifting armies."];
				if ([pHand count] > 0)  {
					[fortifyReviewButton setEnabled:YES];
				}  else  {
					[fortifyReviewButton setEnabled:NO];
				}
				break;
			case P_CHOOSECOUNTRIES:
				view=phaseChooseCountries;
				[nameTextField setStringValue:
						[theGameSetup nameOfPlayer:playerNum]];
				[colorWell setColor:[theGameSetup colorOfPlayer:playerNum]];
				[colorWell display];
				[phaseTextField setStringValue:"Choose Countries"];
				[infoTextField setStringValue:"Before play begins, take turns "
						"choosing the countries on the board."];
				break;
			default:
				break;
		}
	}
	if (view!=nil)  {
		[[controlPanel contentView] addSubview:view];
		[view moveTo:281:8];
		[view display];
	}
	currentPhaseView=view;
	[controlPanel reenableDisplay];
	[controlPanel displayIfNeeded];

	return self;
}

- attackPopupAction:sender
{
	attackOption[currentPlayer]=[[sender selectedCell] tag];
	switch (attackOption[currentPlayer])  {
		case AP_ONCE:
		case AP_UNTILCANT:
			[attackTextField setEnabled:NO];
			[attackSlider setEnabled:NO];
			break;
		case AP_MULTIPLE:
		case AP_UNTILLEFT:
			[attackTextField setEnabled:YES];
			[attackSlider setEnabled:YES];
			break;
	}
	return self;
}

- attackSliderAction:sender
{
	attackNum[currentPlayer]=[sender intValue];
	[attackTextField takeIntValueFrom:sender];
	return self;
}

- turnInCardsAction:sender
{
	int numArmies;
	
	if (gameInProgress)  {
		numArmies=[theCardManager runCardPanel:YES forPlayer:currentPlayer];
		[placeArmyView increaseArmiesLeftBy:numArmies];
		[statusView display];
	}
	
	return self;
}

- reviewCardsAction:sender
{
	int numArmies;
	
	if (gameInProgress)  {
		numArmies=[theCardManager runCardPanel:NO forPlayer:currentPlayer];
	}
	return self;
}

- (BOOL)isGameInProgress
{
	return gameInProgress;
}

- setGameInProgress:(BOOL)flag
{
	gameInProgress=flag;
	return self;
}

- (int)currentPlayer
{
	return currentPlayer;
}

- displayStatusView
{
	[statusView display];
	return self;
}

- setDiceInspector:anObject
{
	diceInspector = anObject;
	return self;
}

@end

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.