This is Country.m in view mode; [Download] [Up]
// Country.m
// Part of Risk by Mike Ferris
#import "Country.h"
#import "mapWraps.h"
#import <stdlib.h>
#import <strings.h>
#import <appkit/Panel.h>
#import <appkit/TextFieldCell.h>
#import <appkit/Font.h>
#import <appkit/Text.h>
#import <appkit/View.h>
#import <objc/List.h>
#define ARMYCELLWIDTH 25.0
#define ARMYCELLHEIGHT 17.0
@implementation Country
// the class needs a single TextFieldCell to draw the army numbers in
// the countries
id armyCell = NULL;
+ initialize
// set the version of the class like any well-behaved class should and
// allocate our text cell class variable
{
if (self == [Country class]) {
[self setVersion:1];
if (armyCell == NULL) {
armyCell = [[TextFieldCell allocFromZone:[self zone]] init];
[armyCell setBackgroundGray:NX_WHITE];
[armyCell setBezeled:NO];
[armyCell setFont:[Font newFont:"Helvetica" size:10.0]];
[armyCell setAlignment:NX_CENTERED];
[armyCell setEditable:NO];
[armyCell setSelectable:NO];
[armyCell setBordered:YES];
[armyCell setTextGray:NX_BLACK];
}
}
return self;
}
- initName:(const char *)nm idNum:(int)id shape:(NXCoord *)s shapePts:(int)sPts
neighbors:(int *)ne neighborNum:(int)nNum
// This method is the designated initializer for the class.
// It allows the specification of everything a simple country needs.
// This is called by the RiskUtil app. Risk reads the country objects
// from a typed stream.
{
self = [super init];
name = NULL;
[self setShape:s shapePts:sPts shape2:NULL shape2Pts:0
shape3:NULL shape3Pts:0];
[self setNeighbors:ne neighborNum:nNum];
[self setName:nm];
[self setIdNum:id];
[self setPlayer:-1 andArmies:0];
[self setArmyCellPtX:0.0 andY:0.0];
turn=-1;
unmovableArmies=0;
return self;
}
- initName:(const char *)nm idNum:(int)id
// cover for DI
{
self = [self initName:nm idNum:id shape:NULL shapePts:0
neighbors:NULL neighborNum:0];
return self;
}
- init
// cover for DI
{
self = [self initName:NULL idNum:-1 shape:NULL shapePts:0
neighbors:NULL neighborNum:0];
return self;
}
- free
// free any allocated memory
{
if (shape) free(shape);
if (shape2) free(shape2);
if (shape3) free(shape3);
if (neighbors) free(neighbors);
if (name) free(name);
return [super free];
}
- windowServerInit
// This method is called by MapView at startup to tell the country to
// define its user path(s) in the window server.
{
if (shape != NULL) {
defineCountry(shape, shapePts, idNum, 1);
}
if (shape2 != NULL) {
defineCountry(shape2, shape2Pts, idNum, 2);
}
if (shape3 != NULL) {
defineCountry(shape3, shape3Pts, idNum, 3);
}
return self;
}
- drawSelfInView:view withColor:(NXColor)color isSelected:(BOOL)sel
// Called by mapview, this method draws the country.
{
// id defaultManager = [view theDefaultManager];
NXRect cellRect = {{0.0, 0.0}, {ARMYCELLWIDTH, ARMYCELLHEIGHT}};
float linewidth, r, g, b;
NXColor tempColor;
// get the border linewidth and border color from the default manager
linewidth = 2.0; //[defaultManager borderWidth];
if (sel) {
tempColor = NX_COLORWHITE; //[defaultManager borderSelectedColor];
} else {
tempColor = NX_COLORBLACK; //[defaultManager borderRegularColor];
}
NXConvertColorToRGB(tempColor, &r, &g, &b);
// draw each shape in the country
NXSetColor(color);
if (shape != NULL) {
drawFromArray(linewidth, r, g, b, idNum, 1);
}
NXSetColor(color);
if (shape2 != NULL) {
drawFromArray(linewidth, r, g, b, idNum, 2);
}
NXSetColor(color);
if (shape3 != NULL) {
drawFromArray(linewidth, r, g, b, idNum, 3);
}
// draw the armies box
if (armies > 1) {
cellRect.origin.x = armyCellPt.x;
cellRect.origin.y = armyCellPt.y;
[armyCell setIntValue:armies-1];
[armyCell drawSelf:&cellRect inView:view];
}
return self;
}
- (BOOL)ptInCountry:(NXPoint *)pt
// returns YES if pt is in the border of this country
{
int in = 0;
if (shape !=NULL) {
hitTestFromArray(pt->x, pt->y, idNum, 1, &in);
}
if ((in==0) && (shape2 !=NULL)) {
hitTestFromArray(pt->x, pt->y, idNum, 2, &in);
}
if ((in==0) && (shape3 !=NULL)) {
hitTestFromArray(pt->x, pt->y, idNum, 3, &in);
}
if (in==1) {
return YES;
} else {
return NO;
}
}
- setPlayer:(int)p andArmies:(int)a
// a common change
{
[self setPlayer:p];
[self setArmies:a];
return self;
}
- (BOOL)isNeighborTo:(int)cNum
// returns YES if we are adjacent to country number cNum (ie
// cNum is in our neighbor list.
{
BOOL found = NO;
int i;
for (i=0;(i<neighborNum && found==NO);i++) {
if (neighbors[i]==cNum) {
found=YES;
}
}
return found;
}
- (BOOL)isConnectedTo:(int)cNum withMapList:ml alreadyTried:(BOOL *)tried
// returns YES if we are connected to country cNum by a contiguous series
// of countries all owned by the same player. This is a recursive routine.
// pass NULL for alreadyTried.
{
int i=0, fnc =0;
BOOL connected = NO, mallocedTried = NO;
int fn[neighborNum];
// if there isn;t already a tried list, make one.
if (tried == NULL) {
int mlc = [ml count];
mallocedTried = YES;
tried = (BOOL *)malloc(sizeof(BOOL) * mlc);
for (i=0;i<mlc;i++) {
tried[i] = NO;
}
}
// first get all the neighbors which are friendly, check to see
// whether the neighbors we look at are the country we are looking
// for.
for (i=0;i<neighborNum;i++) {
if (cNum == neighbors[i]) {
return YES;
}
if ([[ml objectAt:neighbors[i]] player] == player) {
fn[fnc++] = neighbors[i];
}
}
// now fn contains all the country indexes of friendly neighbors
// and if we made it here at all, the country we're looking for is
// not an immediate neighbor. So recurse.
// we've tried our neighbors
tried[idNum]=YES;
// try all our friendly neighbors
for (i=0;((i<fnc) && (!connected));i++) {
if (!tried[fn[i]]) {
// only try if we haven't before
connected = [[ml objectAt:fn[i]] isConnectedTo:cNum withMapList:ml
alreadyTried:tried];
}
}
// now connected should tell us whether it is connected.
// clean up and return
// free tried only if we malloced it.
if (mallocedTried) {
free(tried);
}
return connected;
}
- (const char *)name
{
return name;
}
- (int)idNum
{
return idNum;
}
- (int)player
{
return player;
}
- (int)armies
{
return armies;
}
- (int)movableArmiesForTurn:(int)tNum
// this method is used by the fortify code to make sure no one army can
// scuttle all the way across the map.
{
if (tNum==turn) {
return armies-((unmovableArmies==0)?1:unmovableArmies);
} else {
return armies-1;
}
}
- (int *)getNeighborsCount:(int *)c;
// return our neighbors for more lowlevel processing than we can do.
{
*c = neighborNum;
return neighbors;
}
- getBounds:(NXRect *)b
// return the bounding box for this country. Used for intelligent updating
{
b->origin.x = bounds.origin.x;
b->origin.y = bounds.origin.y;
b->size.width = bounds.size.width;
b->size.height = bounds.size.height;
return self;
}
- setBounds:(NXRect *)b
// set the bounding box for this country. Used for intelligent updating
{
bounds.origin.x = b->origin.x;
bounds.origin.y = b->origin.y;
bounds.size.width = b->size.width;
bounds.size.height = b->size.height;
return self;
}
- calcBounds
// calculate the bounding box for this country
{
NXPoint min, max;
int i;
min.x = armyCellPt.x;
min.y = armyCellPt.y;
max.x = armyCellPt.x + ARMYCELLWIDTH;
max.y = armyCellPt.y + ARMYCELLHEIGHT;
// keep track of the biggest and smallest x and y for all shapes
for (i=0; i<shapePts; i+=2) {
if (shape[i]<min.x) {
min.x = shape[i];
} else if (shape[i]>max.x) {
max.x = shape[i];
}
if (shape[i+1]<min.y) {
min.y = shape[i+1];
} else if (shape[i+1]>max.y) {
max.y = shape[i+1];
}
}
for (i=0; i<shape2Pts; i+=2) {
if (shape2[i]<min.x) {
min.x = shape2[i];
} else if (shape2[i]>max.x) {
max.x = shape2[i];
}
if (shape2[i+1]<min.y) {
min.y = shape2[i+1];
} else if (shape2[i+1]>max.y) {
max.y = shape2[i+1];
}
}
for (i=0; i<shape3Pts; i+=2) {
if (shape3[i]<min.x) {
min.x = shape3[i];
} else if (shape3[i]>max.x) {
max.x = shape3[i];
}
if (shape3[i+1]<min.y) {
min.y = shape3[i+1];
} else if (shape3[i+1]>max.y) {
max.y = shape3[i+1];
}
}
bounds.origin.x = min.x;
bounds.origin.y = min.y;
bounds.size.width = max.x-min.x;
bounds.size.height = max.y-min.y;
return self;
}
- setShape:(NXCoord *)s1 shapePts:(int)sPts
shape2:(NXCoord *)s2 shape2Pts:(int)s2Pts
shape3:(NXCoord *)s3 shape3Pts:(int)s3Pts
// set the shape(s) of this country
{
shape = s1; shapePts=sPts;
shape2 = s2; shape2Pts=s2Pts;
shape3 = s3; shape3Pts=s3Pts;
[self calcBounds];
return self;
}
- setNeighbors:(int *)ne neighborNum:(int)nNum
// set the neighbors of this country
{
neighbors = ne; neighborNum=nNum;
return self;
}
- setName:(const char *)nm
// set the name of this country
{
if (name != NULL) free(name);
if (nm == NULL) {
name = NULL;
} else {
name = (char *)malloc(strlen(nm)+1);
strcpy(name, nm);
}
return self;
}
- setIdNum:(int)id
{
idNum = id;
return self;
}
- setPlayer:(int)p
{
player = p;
return self;
}
- setArmies:(int)a
{
armies = a;
return self;
}
- addArmies:(int)a
{
armies+=a;
return self;
}
- subArmies:(int)a
{
armies-=a;
return self;
}
- addUnmovableArmies:(int)aNum forTurn:(int)tNum
// used by fortify code in conjunction with -movableArmiesForTurn
{
if (tNum==turn) {
unmovableArmies+=aNum;
} else {
unmovableArmies=aNum;
turn=tNum;
}
return self;
}
- setArmyCellPtX:(NXCoord)x andY:(NXCoord)y
// set the lower left corner of the armies box for this country
{
armyCellPt.x=x;
armyCellPt.y=y;
[self calcBounds];
return self;
}
- invalidateSelfInView:view
// mark our bounds as dirty in our map view
{
[view invalidate:&bounds :1];
return self;
}
- write:(NXTypedStream *)typedStream
// write this object to a typed stream
{
[super write:typedStream];
NXWriteType(typedStream, "i", &shapePts);
if (shapePts>0) {
NXWriteArray(typedStream, "f", shapePts, shape);
}
NXWriteType(typedStream, "i", &shape2Pts);
if (shape2Pts>0) {
NXWriteArray(typedStream, "f", shape2Pts, shape2);
}
NXWriteType(typedStream, "i", &shape3Pts);
if (shape3Pts>0) {
NXWriteArray(typedStream, "f", shape3Pts, shape3);
}
NXWriteType(typedStream, "i", &neighborNum);
if (neighborNum>0) {
NXWriteArray(typedStream, "i", neighborNum, neighbors);
}
NXWriteType(typedStream, "*", &name);
NXWriteType(typedStream, "i", &idNum);
NXWriteType(typedStream, "i", &player);
NXWriteType(typedStream, "i", &armies);
NXWritePoint(typedStream, &armyCellPt);
NXWriteRect(typedStream, &bounds);
return self;
}
- read:(NXTypedStream *)typedStream
// read this object from a typed stream
{
[super write:typedStream];
NXReadType(typedStream, "i", &shapePts);
if (shapePts>0) {
shape = (NXCoord *)malloc(shapePts*sizeof(NXCoord));
NXReadArray(typedStream, "f", shapePts, shape);
}
NXReadType(typedStream, "i", &shape2Pts);
if (shape2Pts>0) {
shape2 = (NXCoord *)malloc(shape2Pts*sizeof(NXCoord));
NXReadArray(typedStream, "f", shape2Pts, shape2);
}
NXReadType(typedStream, "i", &shape3Pts);
if (shape3Pts>0) {
shape3 = (NXCoord *)malloc(shape3Pts*sizeof(NXCoord));
NXReadArray(typedStream, "f", shape3Pts, shape3);
}
NXReadType(typedStream, "i", &neighborNum);
if (neighborNum>0) {
neighbors = (int *)malloc(neighborNum*sizeof(int));
NXReadArray(typedStream, "i", neighborNum, neighbors);
}
NXReadType(typedStream, "*", &name);
NXReadType(typedStream, "i", &idNum);
NXReadType(typedStream, "i", &player);
NXReadType(typedStream, "i", &armies);
NXReadPoint(typedStream, &armyCellPt);
NXReadRect(typedStream, &bounds);
turn=-1;
unmovableArmies=0;
return self;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.