This is EmPlayer.m in view mode; [Download] [Up]
//
// $Id: EmPlayer.m,v 1.11 1997/10/31 04:51:45 nygard Exp $
//
//
// This file is a part of Empire, a game of exploration and conquest.
// Copyright (C) 1996 Steve Nygard
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// You may contact the author by:
// e-mail: nygard@telusplanet.net
//
#import "Empire.h"
RCSID ("$Id: EmPlayer.m,v 1.11 1997/10/31 04:51:45 nygard Exp $");
#import "City.h"
#import "EmPlayer.h"
#import "DistributedGameManager.h"
#import "Map.h"
#import "Unit.h"
//======================================================================
// This provides default implementations and useful utility methods
// for players.
//
// Currently, this just has what was needed to develop the Human
// player, so it may need work to make it more useful for developing
// computer players.
//======================================================================
@implementation EmPlayer
- initWithName:(NSString *)aName number:(Player)number world:(Map *)aMap
capital:(City *)capitalCity withEfficiencies:(int)pe:(int)ce
gameManager:(GameManager *)theGameManager
{
EMMapLocation capitalLocation;
int l;
[super init];
gameManager = [theGameManager retain];
playerNumber = number;
map = [aMap copyWithZone:[self zone]];
[map setMapExplored:NO];
capitalLocation = [capitalCity cityLocation];
playerName = [aName retain];
// update map around city.
[map explore3x3AroundLocation:capitalLocation];
productionEfficiency = pe; // 0..100
combatEfficiency = ce;
savedCookie = nil;
cityList = [[NSMutableArray array] retain];
NSAssert (cityList != nil, @"Could not create city array");
unitList = [[NSMutableArray array] retain];
NSAssert (unitList != nil, @"Could not create unit array");
newUnitList = [[NSMutableArray array] retain];
NSAssert (newUnitList != nil, @"Could not create new unit array");
unitsAwaitingMovement = [[NSMutableArray array] retain];
NSAssert (unitsAwaitingMovement != nil, @"Could not create awaiting units array");
selectedUnit = nil;
[self capturedCity:capitalCity];
for (l = 0; l < UNIT_TYPE_COUNT; l++)
{
destroyedUnits[l] = 0;
lostUnits[l] = 0;
}
finalMaps[p_neutral] = nil;
finalMaps[p_player1] = nil;
finalMaps[p_player2] = nil;
finalMaps[p_player3] = nil;
gameOver = NO;
return self;
}
//----------------------------------------------------------------------
- (void) dealloc
{
Player p;
SNRelease (gameManager);
SNRelease (map);
SNRelease (playerName);
SNRelease (savedCookie);
// And all it's cities...
SNRelease (cityList);
// And all it's units...
SNRelease (unitList);
SNRelease (newUnitList);
SNRelease (unitsAwaitingMovement);
for (p = p_neutral; p <= p_player3; p++)
SNRelease (finalMaps[p]);
[super dealloc];
}
//======================================================================
// General data access
//======================================================================
- (GameManager *) gameManager
{
return gameManager;
}
//----------------------------------------------------------------------
- (NSString *) playerName
{
return playerName;
}
//----------------------------------------------------------------------
- (Player) playerNumber
{
return playerNumber;
}
//----------------------------------------------------------------------
- (Map *) map
{
return map;
}
//----------------------------------------------------------------------
- (int) productionEfficiency
{
return productionEfficiency;
}
//----------------------------------------------------------------------
- (int) combatEfficiency
{
return combatEfficiency;
}
//----------------------------------------------------------------------
- (NSArray *) cityList
{
return cityList;
}
//----------------------------------------------------------------------
- (City *) cityAtLocation:(EMMapLocation)target
{
NSEnumerator *cityEnumerator = [cityList objectEnumerator];
EMMapLocation cityLocation;
City *city;
while (city = [cityEnumerator nextObject])
{
cityLocation = [city cityLocation];
if (cityLocation.row == target.row && cityLocation.column == target.column)
return city;
}
return nil;
}
//----------------------------------------------------------------------
// This returns the nearest city, ignoring terrain, for the goHome command.
//----------------------------------------------------------------------
- (City *) ourCityNearestToLocation:(EMMapLocation)target
{
City *nearestCity = nil;
City *city;
NSEnumerator *cityEnumerator = [cityList objectEnumerator];
EMMapLocation cityLocation;
int nearestDistance = 1000000; // Square of nearest distance
int distance, dr, dc;
while (city = [cityEnumerator nextObject])
{
cityLocation = [city cityLocation];
dr = cityLocation.row - target.row;
dc = cityLocation.column - target.column;
distance = dr * dr + dc * dc;
if (distance <= nearestDistance)
{
nearestDistance = distance;
nearestCity = city;
}
}
return nearestCity;
}
//----------------------------------------------------------------------
- (NSArray *) unitList
{
return unitList;
}
//----------------------------------------------------------------------
- (NSArray *) newUnitList
{
return newUnitList;
}
//======================================================================
// Turn phases
//======================================================================
- (void) yourTurn:(int)turn withCookie:(NSNumber *)aCookie
{
NSAssert (savedCookie == nil, @"Cookie is not nil.");
savedCookie = [aCookie retain];
[self yourTurn:turn];
}
//----------------------------------------------------------------------
// This is the one overridden in subclasses.
//----------------------------------------------------------------------
- (void) yourTurn:(int)turn
{
if (turn != 0)
[self initialPhase];
}
//----------------------------------------------------------------------
- (void) turnDone
{
NSAssert (gameOver == NO, @"The game is over.");
// Hopefully, this will retain the object for us, since we release
// it ourselves.
[gameManager performSelector:@selector (turnDone:) withObject:savedCookie afterDelay:0];
// I found a better way of doing this while working on Risk. With that, the
// game manager would first change to the new state, and then execute the
// current state. The execution was performed delayed. This means that
// the player doesn't have to know about this special requirement.
// However, at the moment the game manager code is a bit convoluted in this
// regard.
//[gameManager turnDone:savedCookie];
SNRelease (savedCookie);
}
//----------------------------------------------------------------------
// This is not over-ridden in subclasses.
//----------------------------------------------------------------------
- (void) initialPhase
{
NSEnumerator *cityEnumerator = [cityList objectEnumerator];
NSEnumerator *unitEnumerator;
City *city;
Unit *unit;
[newUnitList removeAllObjects];
while (city = [cityEnumerator nextObject])
{
// This may create a new unit, and add it to the unit list.
[city startOfTurn];
}
NSAssert ([unitsAwaitingMovement count] == 0, @"There were still units awaiting movement!");
// refuel anything in a city or on board a ship
// Reset current range of units
unitEnumerator = [unitList objectEnumerator];
while (unit = [unitEnumerator nextObject])
{
[unit useSkippedFuel];
[unit resetRange];
[unit refuel];
[unitsAwaitingMovement addObject:unit];
}
selectedUnit = nil;
}
//----------------------------------------------------------------------
- (void) addUnit:(Unit *)newUnit
{
[unitList addObject:newUnit];
[newUnitList addObject:newUnit];
}
//----------------------------------------------------------------------
- (void) capturedCity:(City *)aCity
{
EMMapLocation location;
//tmp:
[aCity setOwner:self];
[cityList addObject:aCity];
location = [aCity cityLocation];
// Left for initial (capital) city... otherwise, should be redundant, but harmless.
[map setCityAtLocation:location toPlayer:playerNumber];
}
//----------------------------------------------------------------------
- (void) lostCity:(City *)aCity
{
[aCity cityWasLost];
// Map updating?
[cityList removeObject:aCity];
}
//----------------------------------------------------------------------
// This is required to handle the degenerate case in a distributed game
// where, for example, all three starting cities are next to each other.
//----------------------------------------------------------------------
- (void) updateMapAroundCities
{
NSEnumerator *cityEnumerator = [cityList objectEnumerator];
City *city;
EMMapLocation location;
NSAssert (gameOver == NO, @"The game is over.");
while (city = [cityEnumerator nextObject])
{
location = [city cityLocation];
[gameManager explorePlayer:playerNumber around3x3Location:location updateOtherPlayers:NO];
}
}
//======================================================================
// Map Updating
//======================================================================
- (void) unitIconHasChanged:(Unit *)aUnit
{
EMMapLocation unitLocation = [aUnit unitLocation];
MapToken mapToken = [map tokenAtLocation:unitLocation];
Icon oldIcon = EMIconComponent (mapToken);
if (EMConvertIconToUnitType (oldIcon) == [aUnit unitType])
{
[map setToken:EMChangeIconComponent (mapToken, [aUnit icon]) atLocation:unitLocation];
}
}
//----------------------------------------------------------------------
// Used by the Human player when the production is changed while in the
// Show Production state.
//----------------------------------------------------------------------
- (void) cityProductionChanged:(City *)aCity
{
}
//======================================================================
// Statistics
//======================================================================
- (WarStatistics) warStatistics
{
WarStatistics stats;
int l;
int count;
id city;
int soonest;
int productionType;
UnitType unitType;
//NSAssert (gameOver == NO, @"The game is over.");
stats.world_city_count = [gameManager worldCityCount];
stats.percent_explored = [map percentExplored];
for (l = 0; l < UNIT_TYPE_COUNT; l++)
{
stats.destroyed_units[l] = destroyedUnits[l];
stats.lost_units[l] = lostUnits[l];
stats.under_construction[l] = 0;
stats.in_combat[l] = 0;
stats.soonest_complete[l] = 999;
}
stats.city_count = [cityList count];
for (l = 0; l < stats.city_count; l++)
{
city = [cityList objectAtIndex:l];
productionType = [city productionType];
stats.under_construction[productionType]++;
soonest = [city turnsToConstruct:NULL];
if (soonest < stats.soonest_complete[productionType])
stats.soonest_complete[productionType] = soonest;
}
count = [unitList count];
for (l = 0; l < count; l++)
{
unitType = [[unitList objectAtIndex:l] unitType];
stats.in_combat[unitType]++;
}
return stats;
}
//----------------------------------------------------------------------
- (int) cityCount
{
return [cityList count];
}
//======================================================================
// End of player
//======================================================================
- (BOOL) hasPlayerLost
{
return [cityList count] == 0;
}
//----------------------------------------------------------------------
- (void) gameStopped:(int)activePlayers
{
//NSLog (@"player: %d", playerNumber);
[self createFinalMaps:activePlayers];
gameOver = YES;
}
//----------------------------------------------------------------------
// Returns YES to continue playing, NO to stop game.
//----------------------------------------------------------------------
- (BOOL) playerHasWon:(int)activePlayers
{
[self gameStopped:activePlayers];
return NO;
}
//----------------------------------------------------------------------
- (void) playerHasLost:(int)activePlayers
{
[self gameStopped:activePlayers];
}
//----------------------------------------------------------------------
- (void) playerHasResigned:(int)activePlayers
{
[self gameStopped:activePlayers];
}
//----------------------------------------------------------------------
// Perhaps final set of maps could just be sent, rather than the active players..
//----------------------------------------------------------------------
- (void) createFinalMaps:(int)activePlayers
{
Player p;
EMMapSize mapSize;
int l;
MapToken *canonMapData;
MapToken *mapData;
//NSLog (@"active players: %x", activePlayers);
NSAssert (finalMaps[p_neutral] == nil
&& finalMaps[p_player1] == nil
&& finalMaps[p_player2] == nil
&& finalMaps[p_player3] == nil, @"Final maps have already been set.");
NSAssert (gameOver == NO, @"The game is over.");
// get maps
finalMaps[playerNumber] = [map retain];
for (p = p_player1; p <= p_player3; p++)
{
if (playerNumber != p)
{
//finalMaps[p] = [[gameManager mapForPlayer:p] copyWithZone:[self zone]];
//finalMaps[p] = [[gameManager fetchMapForPlayer:p] copyWithZone:[self zone]];
finalMaps[p] = [[gameManager finalMapForPlayer:p] copyWithZone:[self zone]];
//NSLog (@"finalMaps[%d] retain count: %d", p, [finalMaps[p] retainCount]);
}
}
// Build canon map:
// 1. get terrain, set explored, and strip icons.
finalMaps[p_neutral] = [map copyWithZone:[self zone]];
NSAssert (finalMaps[p_neutral] != nil, @"Could not copy map.");
[finalMaps[p_neutral] stripIcons];
// 2. Put icons for each _ACTIVE_ player back
mapSize = [finalMaps[p_neutral] mapSize];
canonMapData = [finalMaps[p_neutral] mapData];
for (p = p_player1; p <= p_player3; p++)
{
if (finalMaps[p] != nil && (activePlayers & (1 << (p - p_player1))) != 0)
{
//NSLog (@"Combining data from player %d map.", p);
mapData = [finalMaps[p] mapData];
for (l = 0; l < mapSize.width * mapSize.height; l++)
{
if (EMPlayerComponent (mapData[l]) == p)
canonMapData[l] = mapData[l];
}
}
}
[finalMaps[p_neutral] setMapExplored:YES];
}
//======================================================================
// Movement
//======================================================================
- (Unit *) nextUnitAwaitingMovement
{
Unit *nextUnit;
selectedUnit = nil;
nextUnit = nil;
while ([unitsAwaitingMovement count] > 0)
{
nextUnit = [unitsAwaitingMovement objectAtIndex:0];
// Need to handle release of destroyed units in a better way.
if ([nextUnit hasBeenDestroyed] == YES)
{
[unitsAwaitingMovement removeObjectAtIndex:0];
[unitList removeObject:nextUnit]; // Do this in unit...
//[currentUnit free];
//continue;
}
else if ([nextUnit isFinishedMoving] == YES)
{
[unitsAwaitingMovement removeObjectAtIndex:0];
//continue;
}
else
{
selectedUnit = nextUnit;
break;
}
}
return selectedUnit;
}
//----------------------------------------------------------------------
- (Unit *) selectedUnit
{
//NSLog (@"selected unit is %@", selectedUnit);
return selectedUnit;
}
//----------------------------------------------------------------------
- (Unit *) unitWithType:(UnitType)unitType atLocation:(EMMapLocation)target
{
NSEnumerator *unitEnumerator = [unitList objectEnumerator];
EMMapLocation unitLocation;
Unit *unit;
while (unit = [unitEnumerator nextObject])
{
unitLocation = [unit unitLocation];
if (unitLocation.row == target.row && unitLocation.column == target.column && unitType == [unit unitType])
return unit;
}
return nil;
}
//----------------------------------------------------------------------
- (Unit *) primaryUnitAtLocation:(EMMapLocation)target
{
MapToken token = [map tokenAtLocation:target];
UnitType unitType;
unitType = EMConvertIconToUnitType (EMIconComponent (token));
return [self unitWithType:unitType atLocation:target];
}
//======================================================================
// Commands
//======================================================================
- (MoveResult) moveSelectedUnitInDirection:(Direction)dir
{
NSAssert (gameOver == NO, @"The game is over.");
NSAssert (selectedUnit != nil, @"No unit selected.");
return [gameManager moveUnit:selectedUnit inDirection:dir];
}
//----------------------------------------------------------------------
- (BOOL) moveResultDidMove:(MoveResult)moveResult
{
return moveResult == mr_moved || moveResult == mr_victory
|| moveResult == mr_destroyed || moveResult == mr_captured_city;
}
//----------------------------------------------------------------------
- (BOOL) tryToMoveSelectedUnitInDirection:(Direction)dir
{
MoveResult moveResult = [self moveSelectedUnitInDirection:dir];
[self selectedUnitTriedToMove:moveResult];
return [self moveResultDidMove:moveResult];
}
//----------------------------------------------------------------------
- (void) activateThisUnit:(Unit *)aUnit
{
if ([aUnit currentRange] > 0)
{
[unitsAwaitingMovement removeObject:aUnit];
[aUnit setUnitFinishedMoving:NO];
[unitsAwaitingMovement insertObject:aUnit atIndex:0];
//currentUnit = aUnit;
}
}
//----------------------------------------------------------------------
- (void) waitUnit:(Unit *)aUnit
{
[unitsAwaitingMovement removeObject:aUnit];
[unitsAwaitingMovement addObject:aUnit];
}
//======================================================================
// Other
//======================================================================
- (void) destroyUnit:(Unit *)aUnit wasDisbanded:(BOOL)disbanded
{
NSAssert ([aUnit owner] == self, @"We don't own that unit.");
if (aUnit == selectedUnit)
selectedUnit = nil;
[aUnit destroyUnit:disbanded];
if (disbanded == NO)
lostUnits[[aUnit unitType]]++;
[unitsAwaitingMovement removeObject:aUnit];
[newUnitList removeObject:aUnit];
[unitList removeObject:aUnit]; // This should be the last reference.
}
//----------------------------------------------------------------------
- (void) showExplosions:(int)count atLocation:(EMMapLocation)target
{
}
//======================================================================
// Game Resignation
//======================================================================
- (NSArray *) remainingCities
{
NSArray *tmp = [NSArray arrayWithArray:cityList];
//NSLog (@"remaining cities: %@", tmp);
[cityList removeAllObjects];
return tmp;
}
//----------------------------------------------------------------------
- (void) aPlayerHasResigned:(Player)number
{
[map stripIconsOfPlayer:number];
}
//----------------------------------------------------------------------
- (void) resign
{
NSAssert (gameOver == NO, @"The game is already over.");
// This is synchronous, so that when it returns we can safely disconnect.
[gameManager resignPlayerFromGame:playerNumber];
}
//======================================================================
// Subclass Responsibilities
//======================================================================
- (void) selectedUnitTriedToMove:(MoveResult)moveResult
{
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.