home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / OPENSTEP / Games / Empire-0.6-MIS / EditorController.m < prev    next >
Encoding:
Text File  |  1997-10-30  |  26.6 KB  |  1,045 lines

  1. //
  2. // $Id: EditorController.m,v 1.11 1997/10/31 04:51:41 nygard Exp $
  3. //
  4.  
  5. //
  6. //  This file is a part of Empire, a game of exploration and conquest.
  7. //  Copyright (C) 1996  Steve Nygard
  8. //
  9. //  This program is free software; you can redistribute it and/or modify
  10. //  it under the terms of the GNU General Public License as published by
  11. //  the Free Software Foundation; either version 2 of the License, or
  12. //  (at your option) any later version.
  13. //
  14. //  This program is distributed in the hope that it will be useful,
  15. //  but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17. //  GNU General Public License for more details.
  18. //
  19. //  You should have received a copy of the GNU General Public License
  20. //  along with this program; if not, write to the Free Software
  21. //  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22. //
  23. //  You may contact the author by:
  24. //     e-mail:  nygard@telusplanet.net
  25. //
  26.  
  27. #import "Empire.h"
  28.  
  29. RCSID ("$Id: EditorController.m,v 1.11 1997/10/31 04:51:41 nygard Exp $");
  30.  
  31. #import "EditorController.h"
  32.  
  33. #import "Brain.h"
  34. #import "Map.h"
  35. #import "MapView.h"
  36. #import "SNRandom.h"
  37. #import "WorldMapController.h"
  38.  
  39. //======================================================================
  40. // The Editor Controller provides simple "painting" of terrain in
  41. // order to create maps.  There are a couple of different ways of
  42. // "growing" terrain to help.
  43. //
  44. // Additionally, there is some prototype code for trying to automate
  45. // map generation.  I was trying to parameterize it based on the size
  46. // of the map, possibly with the option of being able to select between
  47. // different sets of parameters (i.e. create a world with may small
  48. // islands, or one with a few larger continents, etc.)
  49. //
  50. // I've also tried another method, basically draw a random line through
  51. // the map, raise the elevation on one side, and repeat many times.
  52. // Then create the land based on the elevation.  Unfortunately, while
  53. // this seems to produce nice coastlines, especially with larger maps,
  54. // I end up with one large land mass...  I've got a separate application
  55. // that I was experimenting with this method.
  56. //
  57. // Automated city placement can happen once terrain generation is
  58. // working.
  59. //======================================================================
  60.  
  61. @implementation EditorController
  62.  
  63. - (void) awakeFromNib
  64. {
  65.     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  66.     NSString *imagePath;
  67.     NSImage *image;
  68.     BOOL okay;
  69.     id tmp;
  70.  
  71.     [mapView setCursorEnabled:NO];
  72.  
  73.     imagePath = [[NSBundle mainBundle] pathForImageResource:@"mwi_map_editor.tiff"];
  74.     NSAssert (imagePath != nil, @"Couldn't find mwi_map_editor.tiff");
  75.  
  76.     image = [[[NSImage alloc] initWithContentsOfFile:imagePath] autorelease];
  77.     NSAssert (image != nil, @"Couldn't load mwi_map_editor.tiff");
  78.  
  79.     [mapEditorWindow setMiniwindowImage:image];
  80.     okay = [mapEditorWindow setFrameAutosaveName:@"Map Editor"];
  81.     if (okay == NO)
  82.         NSLog (@"Could not set frame autosave name of editor window.");
  83.  
  84.     okay = [newMapPanel setFrameAutosaveName:@"New Map Panel"];
  85.     if (okay == NO)
  86.         NSLog (@"Could not set frame autosave name of new map panel.");
  87.  
  88.     tmp = [defaults stringForKey:DK_MapWidth];
  89.     [widthTextfield setStringValue:tmp];
  90.  
  91.     tmp = [defaults stringForKey:DK_MapHeight];
  92.     [heightTextfield setStringValue:tmp];
  93. }
  94.  
  95. //----------------------------------------------------------------------
  96.  
  97. - init
  98. {
  99.     NSString *nibFile;
  100.     BOOL loaded;
  101.  
  102.     [super init];
  103.  
  104.     nibFile = @"MapEditor.nib";
  105.     loaded = [NSBundle loadNibNamed:nibFile owner:self];
  106.     if (loaded == NO)
  107.     {
  108.         NSLog (@"Could not load %@.", nibFile);
  109.         [super dealloc];
  110.         return nil;
  111.     }
  112.  
  113.     map = nil;
  114.  
  115.     brushType = 0;
  116.     tokenType = EMCreateMapToken (t_water, p_neutral, i_none, YES);
  117.  
  118.     mapName = nil;
  119.  
  120.     terrainCounts[0] = 0;
  121.     terrainCounts[1] = 0;
  122.     terrainCounts[2] = 0;
  123.     terrainCounts[3] = 0;
  124.  
  125.     lastDirectory = nil;
  126.     rng = [[SNRandom instance] retain];
  127.  
  128.     return self;
  129. }
  130.  
  131. //----------------------------------------------------------------------
  132.  
  133. - (void) dealloc
  134. {
  135.     SNRelease (mapEditorWindow);
  136.     SNRelease (map);
  137.     SNRelease (mapName);
  138.     SNRelease (worldMapController);
  139.     SNRelease (rng);
  140.  
  141.     [super dealloc];
  142. }
  143.  
  144. //----------------------------------------------------------------------
  145.  
  146. - (Map *) map
  147. {
  148.     return map;
  149. }
  150.  
  151. //----------------------------------------------------------------------
  152.  
  153. - (void) setMap:(Map *)newMap
  154. {
  155.     SNRelease (map);
  156.  
  157.     map = newMap;
  158.     [map retain];
  159.  
  160.     [mapView setMap:newMap];
  161.     if (worldMapController != nil)
  162.     {
  163.         [worldMapController setMap:map];
  164.     }
  165. }
  166.  
  167. //----------------------------------------------------------------------
  168.  
  169. - (void) takeBrushTypeFrom:sender
  170. {
  171.     brushType = [sender state];
  172. }
  173.  
  174. //----------------------------------------------------------------------
  175.  
  176. - (void) takeTokenTypeFrom:sender
  177. {
  178.     static Terrain tokens[] = {t_water, t_land, t_city};
  179.   
  180.     tokenType = EMCreateMapToken (tokens[[[sender selectedCell] tag]], p_neutral, i_none, YES); 
  181. }
  182.  
  183. //----------------------------------------------------------------------
  184.  
  185. - (BOOL) validateMenuItem:(NSMenuItem *)menuCell
  186. {
  187.     SEL action = [menuCell action];
  188.     BOOL valid = NO;
  189.  
  190.     if (action == @selector (showWorldMap:))
  191.     {
  192.         valid = YES;
  193.     }
  194.     else if (action == @selector (open:))
  195.     {
  196.         valid = YES;
  197.     }
  198.     else if (action == @selector (save:)
  199.              || action == @selector (saveAs:)
  200.              || action == @selector (saveTo:))
  201.     {
  202.         valid = YES;
  203.     }
  204.  
  205.     return valid;
  206. }
  207.  
  208. //----------------------------------------------------------------------
  209.  
  210. - (void) showWorldMap:sender
  211. {
  212.     if (worldMapController == nil)
  213.     {
  214.         worldMapController = [[WorldMapController alloc] init];
  215.         [worldMapController setMap:map];
  216.         [worldMapController setDelegate:self];
  217.         [worldMapController setTitle:@"Map Editor World Map" autosaveFrame:YES];
  218.     }
  219.  
  220.     [worldMapController showPanel];
  221. }
  222.  
  223. //----------------------------------------------------------------------
  224.  
  225. - (void) newMapStopAction:sender
  226. {
  227.     //[NSApp stopModal];
  228.     [newMapPanel orderOut:self]; 
  229. }
  230.  
  231. //----------------------------------------------------------------------
  232.  
  233. - (void) okayAction:sender
  234. {
  235.     EMMapSize mapSize;
  236.     Map *tmp;
  237.     int count;
  238.   
  239.     mapSize.width = [widthTextfield intValue];
  240.     mapSize.height = [heightTextfield intValue];
  241.  
  242.     if (mapSize.width < 1)
  243.     {
  244.         NSRunAlertPanel (@"New Map", @"The width of the map must be greater than zero.", @"OK", nil, nil);
  245.         return;
  246.     }
  247.  
  248.     if (mapSize.height < 1)
  249.     {
  250.         NSRunAlertPanel (@"New Map", @"The height of the map must be greater than zero.", @"OK", nil, nil);
  251.         return;
  252.     }
  253.  
  254.     [newMapPanel orderOut:self];
  255.  
  256.     count = mapSize.width * mapSize.height;
  257.  
  258.     if (count >= 1000000)
  259.     {
  260.         int alertValue;
  261.  
  262.         alertValue = NSRunAlertPanel (@"Warning", @"Large map size (%d) selected.", @"Cancel", @"Create Map", NULL, count);
  263.  
  264.         if (alertValue != NSAlertAlternateReturn)
  265.             return;
  266.     }
  267.  
  268.     tmp = [[[Map alloc] initMapWithSize:mapSize] autorelease];
  269.     NSAssert (tmp != nil, @"New map is nil");
  270.  
  271.     [tmp clearMapTo:EMCreateMapToken (t_water, p_neutral, i_none, YES)];
  272.  
  273.     [self setMap:tmp];
  274.  
  275.     [mapEditorWindow setTitleWithRepresentedFilename:@"UNTITLED.map"];
  276.     [mapEditorWindow makeKeyAndOrderFront:self];
  277. }
  278.  
  279. //----------------------------------------------------------------------
  280.  
  281. - (void) takeLastDirectoryFromSavePanel:(NSSavePanel *)savePanel
  282. {
  283.     if (lastDirectory != nil)
  284.     {
  285.         SNRelease (lastDirectory);
  286.     }
  287.  
  288.     lastDirectory = [[savePanel directory] retain];
  289. }
  290.   
  291. //----------------------------------------------------------------------
  292.  
  293. - (void) newMap
  294. {
  295.     [newMapPanel makeKeyAndOrderFront:nil];
  296. }
  297.  
  298. //----------------------------------------------------------------------
  299.  
  300. - (void) open:sender
  301. {
  302.     NSArray *types = [NSArray arrayWithObject:@"map"];
  303.     NSOpenPanel *openPanel = [NSOpenPanel openPanel];
  304.     NSString *filename;
  305.     Map *newMap;
  306.  
  307.     [openPanel setDirectory:lastDirectory];
  308.     [openPanel setAllowsMultipleSelection:NO];
  309.  
  310.     if ([openPanel runModalForTypes:types] == NSOKButton)
  311.     {
  312.         [self takeLastDirectoryFromSavePanel:openPanel];
  313.  
  314.         filename = [openPanel filename];
  315.         newMap = [NSUnarchiver unarchiveObjectWithFile:filename];
  316.  
  317.         NSAssert1 (newMap != nil, @"Error loading map '%@'\n", filename);
  318.  
  319.         [self setMap:newMap];
  320.  
  321.         [mapEditorWindow setTitleWithRepresentedFilename:filename];
  322.         [mapEditorWindow makeKeyAndOrderFront:self];
  323.         [mapEditorWindow setDocumentEdited:NO];
  324.  
  325.         SNRelease (mapName);
  326.     
  327.         mapName = [filename retain];
  328.     }
  329. }
  330.  
  331. //----------------------------------------------------------------------
  332.  
  333. - (void) save:sender
  334. {
  335.     [self saveMode:sm_save];
  336. }
  337.  
  338. //----------------------------------------------------------------------
  339.  
  340. - (void) saveAs:sender
  341. {
  342.     [self saveMode:sm_save_as];
  343. }
  344.  
  345. //----------------------------------------------------------------------
  346.  
  347. - (void) saveTo:sender
  348. {
  349.     [self saveMode:sm_save_to];
  350. }
  351.  
  352. //----------------------------------------------------------------------
  353.  
  354. - (BOOL) saveMode:(SaveMode)sm
  355. {
  356.     NSSavePanel *savePanel = [NSSavePanel savePanel];
  357.     NSString *targetFile;
  358.  
  359.     BOOL rflag;
  360.  
  361.     [savePanel setDirectory:lastDirectory];
  362.     [savePanel setRequiredFileType:@"map"];
  363.  
  364.     if (sm == sm_save && mapName == nil)
  365.         sm = sm_save_as;
  366.  
  367.     if (sm == sm_save)
  368.     {
  369.         targetFile = mapName;
  370.     }
  371.     else //(sm != sm_save)
  372.     {
  373.         if ([savePanel runModal] != NSOKButton)
  374.             return NO;
  375.  
  376.         [self takeLastDirectoryFromSavePanel:savePanel];
  377.         targetFile = [savePanel filename];
  378.     }
  379.  
  380.     rflag = [NSArchiver archiveRootObject:map toFile:targetFile];
  381.  
  382.     if (sm == sm_save_as)
  383.     {
  384.         [mapEditorWindow setTitleWithRepresentedFilename:targetFile];
  385.     }
  386.  
  387.     [mapEditorWindow setDocumentEdited:NO];
  388.  
  389.     return rflag;
  390. }
  391.  
  392. //----------------------------------------------------------------------
  393.  
  394. #define FLIP_Y  0x01
  395. #define FLIP_X  0x02
  396. #define FLIP_XY 0x04
  397.  
  398. - (void) growTerrain:(MapToken)token fromLocation:(EMMapLocation)source toLocation:(EMMapLocation)target
  399. {
  400.     int dx;
  401.     int dy;
  402.     int d;
  403.     int increment_E;
  404.     int increment_SE;
  405.     int octant;
  406.     int row = source.row;
  407.     int col = source.column;
  408.     EMMapLocation tmp;
  409.  
  410.     dx = target.column - source.column;
  411.     dy = target.row - source.row;
  412.  
  413.     octant = 0;
  414.  
  415.     if (dy < 0)
  416.     {
  417.         octant |= FLIP_Y;
  418.         dy = -dy;
  419.         row = -row;
  420.         target.row = -target.row;
  421.     }
  422.  
  423.     if (dx < 0)
  424.     {
  425.         octant |= FLIP_X;
  426.         dx = -dx;
  427.         col = -col;
  428.         target.column = -target.column;
  429.     }
  430.  
  431.     if (dx < dy)
  432.     {
  433.         int tmp;
  434.         octant |= FLIP_XY;
  435.         tmp = dx;
  436.         dx = dy;
  437.         dy = tmp;
  438.  
  439.         tmp = row;
  440.         row = col;
  441.         col = tmp;
  442.  
  443.         tmp = target.row;
  444.         target.row = target.column;
  445.         target.column = tmp;
  446.     }
  447.  
  448.     d = 2 * dy - dx;
  449.  
  450.     increment_E = 2 * dy;
  451.     increment_SE = 2 * (dy - dx);
  452.   
  453.     if ([map tokenAtLocation:source] != token)
  454.     {
  455.         [map setToken:token atLocation:source];
  456.     }
  457.     else
  458.     {
  459.         while (col < target.column)
  460.         {
  461.             if (d <= 0)
  462.             {
  463.                 d += increment_E;
  464.                 col++;
  465.             }
  466.             else
  467.             {
  468.                 d += increment_SE;
  469.                 col++;
  470.                 row++;
  471.             }
  472.  
  473.             if (octant & FLIP_XY)
  474.             {
  475.                 tmp.row = col;
  476.                 tmp.column = row;
  477.             }
  478.             else
  479.             {
  480.                 tmp.row = row;
  481.                 tmp.column = col;
  482.             }
  483.  
  484.             if (octant & FLIP_X)
  485.                 tmp.column = -tmp.column;
  486.             if (octant & FLIP_Y)
  487.                 tmp.row = -tmp.row;
  488.  
  489.             if ([map tokenAtLocation:tmp] != token)
  490.             {
  491.                 [map setToken:token atLocation:tmp];
  492.                 break;
  493.             }
  494.         }
  495.     }
  496. }
  497.  
  498. //----------------------------------------------------------------------
  499.  
  500. - (void) branchTerrain:(MapToken)token fromLocation:(EMMapLocation)source toLocation:(EMMapLocation)target
  501. {
  502.     int dx;
  503.     int dy;
  504.     int d;
  505.     int increment_E;
  506.     int increment_SE;
  507.     int octant;
  508.     int row;
  509.     int col;
  510.     EMMapLocation tmp, foo;
  511.     EMMapSize mapSize;
  512.   
  513.     //NSLog (@"source: (%d,%d), target: (%d,%d)", source.row, source.column, target.row, target.column);
  514.  
  515.     // Seed the initial location
  516.     [map setToken:token atLocation:source];
  517. #if 0
  518.     foo = source;
  519.     source = target;
  520.     target = foo;
  521. #endif
  522.     mapSize = [map mapSize];
  523.     row = source.row;
  524.     col = source.column;
  525.     dx = target.column - source.column;
  526.     dy = target.row - source.row;
  527.  
  528.     octant = 0;
  529.  
  530.     if (dy < 0)
  531.     {
  532.         octant |= FLIP_Y;
  533.         dy = -dy;
  534.         row = -row;
  535.         target.row = -target.row;
  536.     }
  537.  
  538.     if (dx < 0)
  539.     {
  540.         octant |= FLIP_X;
  541.         dx = -dx;
  542.         col = -col;
  543.         target.column = -target.column;
  544.     }
  545.  
  546.     if (dx < dy)
  547.     {
  548.         int tmp;
  549.         octant |= FLIP_XY;
  550.         tmp = dx;
  551.         dx = dy;
  552.         dy = tmp;
  553.  
  554.         tmp = row;
  555.         row = col;
  556.         col = tmp;
  557.  
  558.         tmp = target.row;
  559.         target.row = target.column;
  560.         target.column = tmp;
  561.     }
  562.  
  563.     d = 2 * dy - dx;
  564.  
  565.     increment_E = 2 * dy;
  566.     increment_SE = 2 * (dy - dx);
  567.  
  568.     while (col < target.column)
  569.     {
  570.         if (d <= 0)
  571.         {
  572.             d += increment_E;
  573.             col++;
  574.         }
  575.         else
  576.         {
  577.             d += increment_SE;
  578.             col++;
  579.             row++;
  580.         }
  581.  
  582.         if (octant & FLIP_XY)
  583.         {
  584.             tmp.row = col;
  585.             tmp.column = row;
  586.         }
  587.         else
  588.         {
  589.             tmp.row = row;
  590.             tmp.column = col;
  591.         }
  592.  
  593.         if (octant & FLIP_X)
  594.             tmp.column = -tmp.column;
  595.         if (octant & FLIP_Y)
  596.             tmp.row = -tmp.row;
  597.  
  598.         if (tmp.row >= 0 && tmp.column >= 0 && tmp.row < mapSize.height && tmp.column < mapSize.width)
  599.         {
  600.             if ([self location:tmp adjacentToTerrain:EMTerrainComponent (token)] == NO)
  601.             {
  602.                 //[map setToken:token atLocation:tmp];
  603.                 break;
  604.             }
  605.         }
  606.  
  607.         foo = tmp;
  608.     }
  609.  
  610.     if (foo.row >= 0 && foo.column >= 0 && foo.row < mapSize.height && foo.column < mapSize.width)
  611.     {
  612.         [map setToken:token atLocation:foo];
  613.     }
  614. }
  615.  
  616. //----------------------------------------------------------------------
  617.  
  618. - (void) growTerrain:(MapToken)token fromLocation:(EMMapLocation)source
  619. {
  620.     EMMapLocation destination;
  621.     int theta = [rng randomNumberModulo:360];
  622.     double x = (theta * 2 * 3.14159265) / 360;
  623.  
  624.     destination.row = source.row + 1000 * sin (x);
  625.     destination.column = source.column + 1000 * cos (x);
  626.  
  627.     [self growTerrain:token fromLocation:source toLocation:destination];
  628. }
  629.  
  630. //----------------------------------------------------------------------
  631.  
  632. - (void) branchTerrain:(MapToken)token fromLocation:(EMMapLocation)source
  633. {
  634.     EMMapLocation destination;
  635.     int theta = [rng randomNumberModulo:360];
  636.     double x = (theta * 2 * 3.14159265) / 360;
  637.  
  638.     destination.row = source.row + 1000 * sin (x);
  639.     destination.column = source.column + 1000 * cos (x);
  640.  
  641.     [self branchTerrain:token fromLocation:source toLocation:destination];
  642. }
  643.  
  644. //----------------------------------------------------------------------
  645.  
  646. - (BOOL) location:(EMMapLocation)target adjacentToTerrain:(Terrain)terrain
  647. {
  648.     BOOL adjacent = NO;
  649.     MapToken tokens[9];
  650.     int l;
  651.  
  652.     [map get3x3Tokens:tokens aroundLocation:target];
  653.     for (l = 0; l < 9; l++)
  654.     {
  655.         if (EMTerrainComponent (tokens[l]) == terrain)
  656.         {
  657.             adjacent = YES;
  658.             break;
  659.         }
  660.     }
  661.  
  662.     return adjacent;
  663. }
  664.  
  665. //----------------------------------------------------------------------
  666.  
  667. - (void) clearToCurrent:sender
  668. {
  669.     [map clearMapTo:tokenType];
  670. }
  671.  
  672.  
  673. //----------------------------------------------------------------------
  674.  
  675. - (void) suspendMainMapUpdate
  676. {
  677. }
  678.  
  679. //----------------------------------------------------------------------
  680.  
  681. - (void) resumeMainMapUpdate
  682. {
  683. }
  684.  
  685. //======================================================================
  686. // Build World
  687. //======================================================================
  688.  
  689. - (void) recalculateTerrainDistribution:sender
  690. {
  691.     EMMapSize mapSize;
  692.  
  693.     terrainCounts[0] = 0;
  694.     terrainCounts[1] = 0;
  695.     terrainCounts[2] = 0;
  696.     terrainCounts[3] = 0;
  697.  
  698.     if (map != nil)
  699.     {
  700.         mapSize = [map mapSize];
  701.  
  702.         terrainCounts[0] = mapSize.width * mapSize.height;
  703.         terrainCounts[t_water] = [map countTerrainType:t_water];
  704.         terrainCounts[t_land] = [map countTerrainType:t_land];
  705.         terrainCounts[t_city] = [map countTerrainType:t_city];
  706.     }
  707.  
  708.     //NSLog (@"%d, %d, %d, %d", terrainCounts[0], terrainCounts[1], terrainCounts[2], terrainCounts[3]);
  709.  
  710.     [terrainSummaryTableview reloadData];
  711. }
  712.  
  713. //----------------------------------------------------------------------
  714.  
  715. - (void) build:sender
  716. {
  717.     EMMapSize mapSize = [map mapSize];
  718.     EMMapLocation source;
  719.     int islandCount;
  720.     int minimumSize;
  721.     int maximumSize;
  722.     int count;
  723.     int l, m, n;
  724.     int buildType;
  725.     //int lastIndex = 0;
  726.  
  727.     islandCount = [islandCountTextfield intValue];
  728.     minimumSize = [minimumSizeTextfield intValue];
  729.     maximumSize = [maximumSizeTextfield intValue];
  730.     buildType = [[buildTypeMatrix selectedCell] tag];
  731.  
  732.     [mapEditorWindow setDocumentEdited:YES];
  733.     [mapEditorWindow disableFlushWindow];
  734.  
  735.     if (buildType == 0)
  736.     {
  737.         for (l = 0; l < islandCount; l++)
  738.         {
  739.             count = [rng randomNumberBetween:minimumSize:maximumSize];
  740.             source.row = [rng randomNumberModulo:mapSize.height];
  741.             source.column = [rng randomNumberModulo:mapSize.width];
  742.  
  743.             for (m = 0; m < count; m++)
  744.             {
  745.                 //source = [self meanderFromLocation:source lastDirectionIndex:&lastIndex];
  746.                 for (n = 0; n < 2; n++)
  747.                     [self growTerrain:tokenType fromLocation:source];
  748.             }
  749.         }
  750.     }
  751.     else
  752.     {
  753.         for (l = 0; l < islandCount; l++)
  754.         {
  755.             count = [rng randomNumberBetween:minimumSize:maximumSize];
  756.             source.row = [rng randomNumberModulo:mapSize.height];
  757.             source.column = [rng randomNumberModulo:mapSize.width];
  758.  
  759.             for (m = 0; m < count; m++)
  760.             {
  761.                 //source = [self meanderFromLocation:source lastDirectionIndex:&lastIndex];
  762.                 for (n = 0; n < 2; n++)
  763.                     [self branchTerrain:tokenType fromLocation:source];
  764.             }
  765.         }
  766.     }
  767.  
  768.     [mapEditorWindow enableFlushWindow];
  769.     [self recalculateTerrainDistribution:nil];
  770. }
  771.  
  772. //----------------------------------------------------------------------
  773.  
  774. - (void) clearSingleTerrains:sender
  775. {
  776.     EMMapSize mapSize = [map mapSize];
  777.     MapToken tokens[9];
  778.     EMMapLocation target;
  779.     BOOL flag = YES;
  780.     int l;
  781.     int count = 0;
  782.  
  783.     [mapEditorWindow setDocumentEdited:YES];
  784.     for (target.row = 0; target.row < mapSize.height; target.row++)
  785.     {
  786.         for (target.column = 0; target.column < mapSize.width; target.column++)
  787.         {
  788.             flag = YES;
  789.             [map get3x3Tokens:tokens aroundLocation:target];
  790.             if (tokens[4] != EMCreateMapToken (t_city, p_neutral, i_none, YES))
  791.             {
  792.                 count++;
  793.  
  794.                 for (l = 0; l < 9; l++)
  795.                 {
  796.                     if (l != 4 && tokens[l] == tokens[4])
  797.                     {
  798.                         flag = NO;
  799.                         break;
  800.                     }
  801.                 }
  802.  
  803.                 if (flag == YES)
  804.                 {
  805.                     //NSLog (@"tokens: %@", EMFormatNineComponents (tokens));
  806.                     if (EMTerrainComponent (tokens[4]) == t_water)
  807.                         [map setToken:EMCreateMapToken (t_land, p_neutral, i_none, YES) atLocation:target];
  808.                     else
  809.                         [map setToken:EMCreateMapToken (t_water, p_neutral, i_none, YES) atLocation:target];
  810.                         
  811.                     //[map setToken:tokens[0] atLocation:target];
  812.                 }
  813.             }
  814.         }
  815.     }
  816. }
  817.  
  818. //----------------------------------------------------------------------
  819.  
  820. - (EMMapLocation) meanderFromLocation:(EMMapLocation)location
  821.                    lastDirectionIndex:(int *)lastIndex
  822.                                origin:(EMMapLocation)origin
  823.                                  size:(EMMapSize)size
  824. {
  825.     EMMapLocation target;
  826.  
  827.     target = [self meanderFromLocation:location lastDirectionIndex:lastIndex];
  828.  
  829.     if (target.row < origin.row || target.column < origin.column
  830.         || target.row > origin.row + size.height || target.column > origin.column + size.width)
  831.     {
  832.         *lastIndex = (*lastIndex + 4) % 9;
  833.     }
  834.  
  835.     return target;
  836. }
  837.  
  838. //----------------------------------------------------------------------
  839.  
  840. - (EMMapLocation) meanderFromLocation:(EMMapLocation)location lastDirectionIndex:(int *)lastIndex
  841. {
  842.     //EMMapSize mapSize = [map mapSize];
  843.  
  844.     Direction directions[8] = {d_northwest, d_north, d_northeast, d_east, d_southeast, d_south, d_southwest, d_west};
  845.     Direction dir;
  846.     int turn;
  847.  
  848.     int dy[9] = {-1, -1, -1,  0,  0,  0,  1,  1,  1};
  849.     int dx[9] = {-1,  0,  1, -1,  0,  1, -1,  0,  1};
  850.  
  851.     turn = [rng randomNumberBetween:-1:1];
  852.     turn += *lastIndex;
  853.  
  854.     if (turn < 0)
  855.         turn += 8;
  856.     else if (turn > 7)
  857.         turn -= 8;
  858.  
  859.     dir = directions[turn];
  860.     *lastIndex = turn;
  861.  
  862.     location.row += dy[dir];
  863.     location.column += dx[dir];
  864.  
  865.     return location;
  866. }
  867.  
  868. //----------------------------------------------------------------------
  869.  
  870. - (void) meander:sender
  871. {
  872.     EMMapSize mapSize = [map mapSize];
  873.     EMMapLocation source;
  874.     int buildType;
  875.     int m;
  876.     int lastIndex = 0;
  877.  
  878.     [mapEditorWindow setDocumentEdited:YES];
  879.  
  880.     buildType = [[buildTypeMatrix selectedCell] tag];
  881.     source.row = [rng randomNumberModulo:mapSize.height];
  882.     source.column = [rng randomNumberModulo:mapSize.width];
  883.  
  884.     [map setToken:tokenType atLocation:source];
  885.     for (m = 0; m < 20; m++)
  886.     {
  887.         source = [self meanderFromLocation:source lastDirectionIndex:&lastIndex];
  888.         [map setToken:tokenType atLocation:source];
  889.     }
  890. }
  891.  
  892. //----------------------------------------------------------------------
  893.  
  894. - (int) numberOfRowsInTableView:(NSTableView *)aTableView
  895. {
  896.     return 4;
  897. }
  898.  
  899. //----------------------------------------------------------------------
  900.  
  901. - tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
  902. {
  903.     NSString *terrainNames[] = {
  904.         @"Total",
  905.         @"Water",
  906.         @"Land",
  907.         @"City"
  908.     };
  909.     id identifier;
  910.     id tmp = nil;
  911.     Terrain terrain;
  912.  
  913.     identifier = [aTableColumn identifier];
  914.     terrain = (rowIndex >= 3) ? t_unknown : t_water + rowIndex;
  915.  
  916.     if ([identifier isEqual:@"Terrain"] == YES)
  917.     {
  918.         tmp = terrainNames[terrain];
  919.     }
  920.     else if ([identifier isEqual:@"Count"] == YES)
  921.     {
  922.         tmp = [NSNumber numberWithInt:terrainCounts[terrain]];
  923.     }
  924.     else if ([identifier isEqual:@"Percent"] == YES)
  925.     {
  926.         if (terrainCounts[t_unknown] != 0)
  927.             tmp = [NSString stringWithFormat:@"%.2f", terrainCounts[terrain] * 100.0 / terrainCounts[t_unknown]];
  928.     }
  929.  
  930.     return tmp;
  931. }
  932.  
  933. //======================================================================
  934. // MapView Delegate
  935. //======================================================================
  936.  
  937. - (void) mouseDown:(unsigned int)modifierFlags atLocation:(EMMapLocation)target
  938. {
  939.     EMMapLocation destination;
  940.     
  941.     [mapEditorWindow setDocumentEdited:YES];
  942.     [mapView scrollLocationToVisible:target];
  943.  
  944.     if ((modifierFlags & NSControlKeyMask) != 0)
  945.     {
  946.         int theta = [rng randomNumberModulo:360];
  947.         double x = (theta * 2 * 3.14159265) / 360;
  948.  
  949.         destination.row = target.row + 1000 * sin (x);
  950.         destination.column = target.column + 1000 * cos (x);
  951.         [self growTerrain:tokenType fromLocation:target toLocation:destination];
  952.  
  953.         //[self growTerrain:tokenType fromLocation:target toLocation:0:0];
  954.         //[self growTerrain:tokenType fromLocation:target toLocation:49:79];
  955.         //[self growTerrain:tokenType fromLocation:target toLocation:49:0];
  956.         //[self growTerrain:tokenType fromLocation:target toLocation:destrow:destcol];
  957.     }
  958.     else if ((modifierFlags & NSCommandKeyMask) != 0)
  959.     {
  960.         int theta = [rng randomNumberModulo:360];
  961.         double x = (theta * 2 * 3.14159265) / 360;
  962.  
  963.         destination.row = target.row + 1000 * sin (x);
  964.         destination.column = target.column + 1000 * cos (x);
  965.         [self branchTerrain:tokenType fromLocation:target toLocation:destination];
  966.  
  967.         //[self growTerrain:tokenType fromLocation:target toLocation:0:0];
  968.         //[self growTerrain:tokenType fromLocation:target toLocation:49:79];
  969.         //[self growTerrain:tokenType fromLocation:target toLocation:49:0];
  970.         //[self growTerrain:tokenType fromLocation:target toLocation:destrow:destcol];
  971.     }
  972.     else
  973.     {
  974.         switch (brushType)
  975.         {
  976.           case 0:
  977.               [map setToken:tokenType atLocation:target];
  978.               break;
  979.  
  980.           default:
  981.               [map set3x3TokensTo:tokenType aroundLocation:target];
  982.               break;
  983.         }
  984.     } 
  985. }
  986.  
  987. //----------------------------------------------------------------------
  988.  
  989. - (void) mouseUp:(unsigned int)modifierFlags atLocation:(EMMapLocation)target
  990. {
  991. }
  992.  
  993. //----------------------------------------------------------------------
  994.  
  995. - (void) rightMouseDown:(unsigned int)modifierFlags atLocation:(EMMapLocation)target
  996. {
  997. }
  998.  
  999. //----------------------------------------------------------------------
  1000.  
  1001. - (void) rightMouseUp:(unsigned int)modifierFlags atLocation:(EMMapLocation)target
  1002. {
  1003. }
  1004.  
  1005. //----------------------------------------------------------------------
  1006.  
  1007. - (void) keyDown:(NSEvent *)theEvent
  1008. {
  1009. }
  1010.  
  1011. //======================================================================
  1012. // Window Delegate
  1013. //======================================================================
  1014.  
  1015. - (void) windowDidBecomeKey:(NSNotification *)notification
  1016. {
  1017. }
  1018.  
  1019. //----------------------------------------------------------------------
  1020.  
  1021. - (void) windowDidResignKey:(NSNotification *)notification
  1022. {
  1023. }
  1024.  
  1025. //----------------------------------------------------------------------
  1026.  
  1027. - (void) windowWillMiniaturize:(NSNotification *)notification
  1028. {
  1029.     NSWindow *theWindow = [notification object];
  1030.  
  1031.     [theWindow setAutodisplay:NO];
  1032. }
  1033.  
  1034. //----------------------------------------------------------------------
  1035.  
  1036. - (void) windowDidDeminiaturize:(NSNotification *)notification
  1037. {
  1038.     NSWindow *theWindow = [notification object];
  1039.  
  1040.     [theWindow setAutodisplay:YES];
  1041.     [theWindow displayIfNeeded];
  1042. }
  1043.  
  1044. @end
  1045.