This is Country.m in view mode; [Download] [Up]
// Country.m // Part of Risk by Mike Ferris #import "Country.h" #import "mapWraps.h" #import "MapView.h" #import "DefaultManager.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 = [defaultManager borderWidth]; if (sel) { tempColor = [defaultManager borderSelectedColor]; } else { tempColor = [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.