This is City.m in view mode; [Download] [Up]
//
// $Id: City.m,v 1.13 1997/10/31 04:51:34 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: City.m,v 1.13 1997/10/31 04:51:34 nygard Exp $");
#import "City.h"
#import "Map.h"
#import "EmPlayer.h"
#import "Unit.h"
#import "Orders.subproj/Orders.h"
#import "PathSegment.h"
static int _cityNumber = 0;
#if 1
// Production times for 50 % production efficiency.
int unit_production_times[][2] =
{
// initial production, subsequent production
{ 0, 0 }, // u_unknown
{ 6, 5 }, // u_army
{ 12, 10 }, // u_fighter
{ 30, 25 }, // u_transport
{ 24, 20 }, // u_submarine
{ 24, 20 }, // u_destroyer
{ 42, 35 }, // u_cruiser
{ 48, 40 }, // u_carrier
{ 60, 50 }, // u_battleship
{ 24, 1000 } // u_hovercraft
};
#else
// These are more suitable for testing...
int unit_production_times[][2] =
{
// initial production, subsequent production
{ 0, 0 }, // u_unknown
{ 2, 1 }, // u_army
{ 2, 1000 }, // u_fighter
{ 2, 100 }, // u_transport
{ 2, 200 }, // u_submarine
{ 2, 200 }, // u_destroyer
{ 2, 350 }, // u_cruiser
{ 48, 40 }, // u_carrier
{ 60, 50 }, // u_battleship
{ 1, 1000 } // u_hovercraft
};
#endif
//======================================================================
// Cities produce units for a player. They may be sent across the
// wire in distributed games.
//
// The encoding needs to be revised if we want to support saved games.
//======================================================================
#define City_VERSION 1
@implementation City
+ (void) initialize
{
if (self == [City class])
{
[self setVersion:City_VERSION];
}
}
//----------------------------------------------------------------------
// Perhaps a city shouldn't be required to know about maps and tokens...
//----------------------------------------------------------------------
- initAtLocation:(EMMapLocation)theCityLocation ofMap:(Map *)map
{
MapToken mapTokens[9];
int l;
[super init];
cityLocation = theCityLocation;
// Mutable?
cityName = [[NSString stringWithFormat:@"City #%d", _cityNumber++] retain];
NSAssert (cityName != nil, @"City name is nil.");
owner = nil;
cityUnits = [[NSMutableArray array] retain];
port = NO;
// Perhaps the map should be updated first?
for (l = 0; l < 9; l++)
mapTokens[l] = -1;
[map get3x3Tokens:mapTokens aroundLocation:cityLocation];
for (l = 0; l < 9; l++)
{
if (mapTokens[l] != -1 && EMTerrainComponent (mapTokens[l]) == t_water)
port = YES;
}
observers = [[NSMutableArray array] retain];
flightPath = nil;
productionType = u_unknown;
firstUnit = YES;
[self setProduction:u_army];
return self;
}
//----------------------------------------------------------------------
- (void) dealloc
{
SNRelease (cityName);
SNRelease (cityUnits);
SNRelease (observers);
SNRelease (flightPath);
[super dealloc];
}
//----------------------------------------------------------------------
- (void) encodeWithCoder:(NSCoder *)aCoder
{
//NSAssert (owner == nil, @"Won't encode owner.");
[super encodeWithCoder:aCoder];
[aCoder encodeValuesOfObjCTypes:"iic", &cityLocation.row, &cityLocation.column, &port];
[aCoder encodeObject:cityName];
// Cities are only encoded when they've been captured -- production type, and
// other player specific things need not be encoded.
}
//----------------------------------------------------------------------
- initWithCoder:(NSCoder *)aDecoder
{
[super initWithCoder:aDecoder];
[aDecoder decodeValuesOfObjCTypes:"iic", &cityLocation.row, &cityLocation.column, &port];
cityName = [[aDecoder decodeObject] retain];
owner = nil;
cityUnits = [[NSMutableArray array] retain];
observers = [[NSMutableArray array] retain];
productionType = u_unknown;
firstUnit = YES;
[self setProduction:u_army];
return self;
}
//----------------------------------------------------------------------
// We really do want cities sent bycopy.
//----------------------------------------------------------------------
- replacementObjectForPortCoder:(NSPortCoder *)encoder
{
if ([encoder isBycopy] == YES)
return self;
return [super replacementObjectForPortCoder:encoder];
}
//----------------------------------------------------------------------
- (NSString *) description
{
return [NSString stringWithFormat:@"%@: %@ at (%d,%d), owner: %@",
NSStringFromClass ([self class]), cityName, cityLocation.row, cityLocation.column, owner];
}
//----------------------------------------------------------------------
- (EMMapLocation) cityLocation
{
return cityLocation;
}
//----------------------------------------------------------------------
- (BOOL) isAPort
{
return port;
}
//----------------------------------------------------------------------
- (NSString *) cityName
{
return cityName;
}
//----------------------------------------------------------------------
- (void) setCityName:(NSString *)aName
{
SNRelease (cityName);
cityName = aName;
[cityName retain];
}
//----------------------------------------------------------------------
- (EmPlayer *) owner
{
return owner;
}
//----------------------------------------------------------------------
- (void) setOwner:(EmPlayer *)aPlayer
{
owner = aPlayer;
}
//----------------------------------------------------------------------
// Figures out the production efficiency based on current owner.
//----------------------------------------------------------------------
- (int) productionEfficiency
{
if (owner == nil)
return 50;
return [owner productionEfficiency];
}
//----------------------------------------------------------------------
- (float) productionTimeMultiplier
{
int pe = [self productionEfficiency];
float r;
// Arbitrary value to avoid division by zero.
if (pe == 0)
r = 100;
else
r = 50.0 / pe;
return r;
}
//----------------------------------------------------------------------
- (int) turnsToConstruct:(int *)turnArray
{
float ptm = [self productionTimeMultiplier];
int l;
if (turnArray != NULL)
{
for (l = u_army; l <= u_hovercraft; l++)
turnArray[l - u_army] = unit_production_times[l][0] * ptm;
if (productionType != u_unknown)
turnArray[productionType - u_army] = turnsUntilConstructed;
}
return turnsUntilConstructed;
}
//----------------------------------------------------------------------
- (void) defaultTurnsToConstruct:(int *)turnArray
{
float ptm = [self productionTimeMultiplier];
int l;
for (l = u_army; l <= u_hovercraft; l++)
turnArray[l - u_army] = unit_production_times[l][0] * ptm;
}
//----------------------------------------------------------------------
- (NSArray *) cityUnits
{
return cityUnits;
}
//----------------------------------------------------------------------
- (UnitType) productionType
{
return productionType;
}
//----------------------------------------------------------------------
- (void) setProduction:(UnitType)newProductionType
{
NSAssert (newProductionType >= u_army && newProductionType <= u_hovercraft, @"Invalid unit type.");
if (productionType == newProductionType)
return;
firstUnit = YES;
productionType = newProductionType;
if (productionType != u_unknown)
turnsUntilConstructed = unit_production_times[productionType][0] * [self productionTimeMultiplier];
else
turnsUntilConstructed = 0;
if (owner != nil)
[owner cityProductionChanged:self];
}
//======================================================================
// Turn phases
//======================================================================
- (void) startOfTurn
{
NSEnumerator *unitEnumerator;
Unit *unit;
unitEnumerator = [cityUnits objectEnumerator];
while (unit = [unitEnumerator nextObject])
{
[unit repairDamage];
//[unit refuel];
}
if (productionType == u_unknown)
{
[self setProduction:u_army];
}
else if (productionType >= u_army && productionType <= u_hovercraft)
{
// Produce new units...
turnsUntilConstructed--;
if (turnsUntilConstructed == 0)
{
Unit *newUnit;
// Create unit
newUnit = [[Unit alloc] initWithUnitType:productionType inCity:self];
//NSLog (@"%@ produced %@", self, newUnit);
if (productionType == u_fighter && flightPath != nil)
{
OMoveTo *tmpOrder = [flightPath copy];
[tmpOrder setUnit:newUnit];
[newUnit setOrder:tmpOrder];
//printf ("Orders(1) for '%s' set to a flight path...\n", [newUnit unitName]);
}
// Add to player, city
[owner addUnit:newUnit];
//[self unitDidEnter:newUnit]; // done in init
// reset production
firstUnit = NO;
turnsUntilConstructed = unit_production_times[productionType][1] * [self productionTimeMultiplier];
}
}
}
//======================================================================
// Observer Pattern
//======================================================================
- (void) attach:observer
{
[observers addObject:observer];
}
//----------------------------------------------------------------------
- (void) detach:observer
{
[observers removeObject:observer];
}
//----------------------------------------------------------------------
- (void) unitDidExit:(Unit *)aUnit
{
NSEnumerator *objectEnumerator;
id tmp;
[cityUnits removeObject:aUnit];
objectEnumerator = [observers objectEnumerator];
while (tmp = [objectEnumerator nextObject])
{
if ([tmp respondsToSelector:@selector (unitDidExit:)] == YES)
[tmp unitDidExit:aUnit];
}
}
//----------------------------------------------------------------------
- (void) unitDidEnter:(Unit *)aUnit
{
NSEnumerator *objectEnumerator;
id tmp;
[cityUnits addObject:aUnit];
// Send fighters that enter the city along the flight path.
if ([aUnit unitType] == u_fighter && flightPath != nil)
{
OMoveTo *tmpOrder = [flightPath copy];
[tmpOrder setUnit:aUnit];
[aUnit setOrder:tmpOrder];
}
objectEnumerator = [observers objectEnumerator];
while (tmp = [objectEnumerator nextObject])
{
if ([tmp respondsToSelector:@selector (unitDidEnter:)] == YES)
[tmp unitDidEnter:aUnit];
}
}
//----------------------------------------------------------------------
- (void) cityWasLost
{
NSEnumerator *objectEnumerator;
id tmp;
Unit *unit;
// Notify all observers. Observers don't detach -- they are
// detached here.
objectEnumerator = [observers objectEnumerator];
while (tmp = [objectEnumerator nextObject])
{
if ([tmp respondsToSelector:@selector (cityWasLost)] == YES)
[tmp cityWasLost];
}
[observers removeAllObjects];
// Now, destroy all units within city.
unit = [cityUnits lastObject];
while (unit != nil)
{
[owner destroyUnit:unit wasDisbanded:NO];
//[cityUnits removeLastObject];
unit = [cityUnits lastObject];
}
// Reset instance vars to default.
owner = nil;
productionType = u_unknown;
firstUnit = YES;
[self setProduction:u_army];
}
//----------------------------------------------------------------------
- (void) unitIconHasChanged:(Unit *)aUnit
{
NSEnumerator *objectEnumerator;
id tmp;
objectEnumerator = [observers objectEnumerator];
while (tmp = [objectEnumerator nextObject])
{
if ([tmp respondsToSelector:@selector (unitIconHasChanged:)] == YES)
[tmp unitIconHasChanged:aUnit];
}
}
//----------------------------------------------------------------------
- (PathSegment *) flightPathInMapView:(MapView *)mapView
{
PathSegment *path = nil;
if (flightPath != nil)
{
path = [[PathSegment alloc] initPathFromLocation:cityLocation
toLocation:[flightPath destination]
in:mapView];
}
return path;
}
//----------------------------------------------------------------------
- (void) setFlightPath:(OMoveTo *)newFlightPath
{
SNRelease (flightPath);
flightPath = [newFlightPath retain];
}
@end
//======================================================================
@implementation City (Extensions)
- (NSString *) attributesInspectorClassName
{
return @"CityAttributesInspector";
}
//----------------------------------------------------------------------
- (NSString *) unitsInspectorClassName
{
return @"CityUnitsInspector";
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.