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.