Advertisement
petethepoet

Unit1.pas (2) of dtm_maker project

May 6th, 2011
93
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Pascal 16.77 KB | None | 0 0
  1. unit Unit1;
  2.  
  3. {$R *.lfm}
  4.  
  5. {$MODE Delphi}
  6.  
  7. interface
  8.  
  9. uses
  10.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, Menus,
  11.   StdCtrls, LResources;
  12.  
  13. {  Digital Terrain Model Maker - Island Maker
  14.  
  15.    Language: Lazarus
  16.    Author:   Peter E. Williams
  17.    Date:     12 March 2011
  18.    Version:  0.01 beta 2
  19.  
  20.    Description: The original idea for this program was to write a random
  21.      island DTM generator which goes on a random walk defining the
  22.      _outline_ of the island. Therefore, it was my original intention
  23.      that areas of "sea" which are land-locked should in some later process
  24.      be marked as land (since the program is really only defining a
  25.      coast-line).
  26.  
  27.      Island map is generated starting with a "*" and ending with a "@".
  28.      All points in between go in the sequence A..Z..1..9 and repeat. This
  29.      is simply to show the sequence in which the points were generated.
  30.  
  31.      Next steps would required an expanded "map_detail" record.
  32.  
  33.      The classical next steps would be to:
  34.         (a) mark land-locked water as land,
  35.         (b) randomly pick a point of land then go on a "short" random
  36.             walk to define the top of a mountain range,
  37.         (c) do same as (b) but for a valley,
  38.         (d) run an algorithm to graduate the elevation levels of land
  39.             between mountain range and valley.
  40.         (e) generate a few number of random spots and define them
  41.             as towns.
  42.         (f) do a series of "short" random walks (similar to (b)) to
  43.             define vegetation types, roads, railroads, rivers, etc.
  44.             Obviously river flows would be determined by elevation
  45.             levels. Logically roads and railroads would connect towns
  46.             [placed in (e)] so would be logical to _start_
  47.             roads/railroads random walks at these points.
  48.  
  49.      I have had a good book which discusses these types of algorithms
  50.      which I bought many years ago... it's called "Games Programming".
  51.      If anyone's interested I'll be happy to post the details of it
  52.      when I get home - or I'll bring it along to the next SIG I go to.
  53.  
  54.           UP
  55.           9
  56.       NW  N  NE
  57.         8 1 2
  58.       W 7   3 E
  59.         6 5 4
  60.       SW  S  SE
  61.           10
  62.          DOWN
  63.  
  64. }
  65.  
  66. const
  67.   array_max_x = 1000;
  68.   array_max_y = 1000;
  69.   default_max_x = 90; // horizontal
  70.   default_max_y = 35; // vertical
  71.   max_directions = 10;
  72.  
  73. type
  74.   directions = 1..max_directions;
  75.   directions_text_type = array[directions] of string;
  76.  
  77. const
  78.   directions_text : directions_text_type =
  79.     ('North', 'North-East', 'East', 'South-East', 'South', 'South-West', 'West', 'North-West', 'Up', 'Down' );
  80.  
  81. type
  82.   Terrain_type = (Land, Sea);
  83.  
  84.   exit_type = array[ directions ] of integer;
  85.  
  86.   description_type = array[1..2] of string;
  87.  
  88.   map_detail = record
  89.                  FTerrain : Terrain_type;
  90.                  FTerrain_char : char;
  91.                  Fdescription : description_type;
  92.                  Fx, Fy, Flocation_number : integer;
  93.                  Fexits : exit_type;
  94.                end;
  95.  
  96.   // The Island class definition
  97.   TIsland = Generic
  98.   TList(map_detail);
  99.     Items = array of map_detail;
  100.   end;
  101.  
  102.   island_2D_type = array[ 1..array_max_x, 1..array_max_y ] of
  103.                      map_detail;
  104.  
  105. {
  106.   island_single_type = array[ 1..(array_max_x * array_max_y) ] of
  107.                          map_detail;
  108. }
  109.  
  110.   // The form class definition
  111.  
  112.   { TForm1 }
  113.  
  114.   TForm1 = class(TForm)
  115.     File1: TMenuItem;
  116.     MainMenu1: TMainMenu;
  117.     CreateANewIsland: TMenuItem;
  118.     Memo1: TMemo;
  119.     Options: TMenuItem;
  120.     SaveIslandToFile: TMenuItem;
  121.     SaveDialog1: TSaveDialog;
  122.     procedure CreateANewIslandClick(Sender: TObject);
  123.     procedure OptionsClick(Sender: TObject);
  124.     procedure SaveIslandToFileClick(Sender: TObject);
  125.     procedure show_island( start_x, start_y, finish_x, finish_y, max_locations : integer);
  126.     procedure make_an_island;
  127.     procedure FormCreate(Sender: TObject);
  128.   private
  129.     { Private declarations }
  130.     // The TList object we use in this code
  131.  
  132.     // Method to show the contents of our list object
  133.     procedure ShowListContents;
  134.  
  135.     { Public declarations }
  136.   public
  137.  
  138.   end;
  139.  
  140. var
  141.   Form1: TForm1;
  142.   Island : TIsland;
  143.   Island_2D : island_2D_type;
  144.   max_x, max_y,
  145.   location_counter,
  146.   max_locations,
  147.   start_x, start_y,
  148.   finish_x, finish_y : integer;
  149.  
  150. implementation
  151.  
  152. uses set_max;
  153.  
  154. {$R *.lfm}
  155.  
  156. // TList sort routine : compare Islands by name
  157. // --------------------------------------------------------------------------
  158. // The returned integer has the following value :
  159. //
  160. //   > 0 : (positive)   Item1 is less than Item2
  161. //     0 : Item1 is equal to Item2
  162. //   < 0 : (negative)   Item1 is greater than Item2
  163. function compareByLocationNumber(Item1 : Pointer; Item2 : Pointer) : Integer;
  164. var
  165.   Island1, Island2 : TIsland;
  166. begin
  167.   // We start by viewing the object pointers as TIsland objects
  168.   Island1 := TIsland(Item1);
  169.   Island2 := TIsland(Item2);
  170.  
  171.   // Now compare by string
  172.   if Island1.Flocation_number > Island2.Flocation_number then
  173.     Result := 1
  174.   else
  175.     if Island1.Flocation_number = Island2.Flocation_number then
  176.       Result := 0
  177.     else
  178.       Result := -1;
  179. end;
  180. {--------------------------------------------------------------------}
  181.  
  182. procedure blank_island;
  183. var
  184.   j,k,d : integer;
  185. begin
  186.   for j := 1 to max_x do
  187.     for k := 1 to max_y do
  188.     begin
  189.       Island_2D[ j,k ].FTerrain := Sea;
  190.       Island_2D[ j,k ].FTerrain_char := '.';
  191.       Island_2D[ j,k ].Fdescription[1] := '';
  192.       Island_2D[ j,k ].Fdescription[2] := '';
  193.       Island_2D[ j,k ].Fx := 0;
  194.       Island_2D[ j,k ].Fy := 0;
  195.       Island_2D[ j,k ].Flocation_number := 0;
  196.       for d := 1 to max_directions do
  197.         Island_2D[ j,k ].Fexits[ d ] := 0;
  198.     end;
  199.   max_locations := trunc( max_x * max_y / 1.575 ); // gives 2000 for 35 x 90
  200. end; { blank_island }
  201. {--------------------------------------------------------------------}
  202.  
  203. {
  204.           UP
  205.           9
  206.       NW  N  NE
  207.         8 1 2
  208.       W 7   3 E
  209.         6 5 4
  210.       SW  S  SE
  211.           10
  212.          DOWN
  213. }
  214.  
  215. procedure define_island_outline( var start_x, start_y,
  216.   finish_x, finish_y: integer;
  217.   var location_counter, max_locations : integer );
  218. var
  219.   got_good_dir : boolean;
  220.   old_x, old_y,
  221.   j,
  222.   current_x, current_y : integer;
  223.   new_direction_int,
  224.   old_direction_int,
  225.   opposite_direction : directions;
  226.   char1 : char;
  227.   temp_direction : directions;
  228. begin
  229.   current_x := random( max_x ) + 1;
  230.   current_y := random( max_y ) + 1;
  231.   start_x := current_x;
  232.   start_y := current_y;
  233.   location_counter := 1;
  234.   old_x := 0;
  235.   old_y := 0;
  236.   old_direction_int := 1; //north
  237.   temp_direction := 1; //north
  238.   char1 := '9'; // this will mean that the sequence starts with an "A"
  239.   Island_2D[ current_x, current_y ].FTerrain := Land;
  240.   Island_2D[ current_x, current_y ].FTerrain_char := '*';
  241.   max_locations := trunc( max_x * max_y / 1.575 ); // gives 2000 for 35 x 90
  242.  
  243.   j := 1;
  244.   while (j <= max_locations) do
  245.   begin
  246.     repeat
  247. {
  248.           UP
  249.           9
  250.       NW  N  NE
  251.         8 1 2
  252.       W 7   3 E
  253.         6 5 4
  254.       SW  S  SE
  255.           10
  256.          DOWN
  257. }
  258.       new_direction_int := random( max_directions ) + 1;
  259.       got_good_dir := true;
  260.  
  261.       // we don't want to go straight back where we came from
  262.       if abs(new_direction_int - old_direction_int) = 4 then
  263.         got_good_dir := false;
  264.  
  265.       if ((old_direction_int = 9) and (new_direction_int = 10)) or
  266.          ((old_direction_int = 10) and (new_direction_int = 9)) then
  267.         got_good_dir := false;
  268.  
  269.       // next 4 tests are to keep from going outside the bounds of the map.
  270.       if (new_direction_int in [ 2..4 ]) and (current_x >= max_x) then
  271.         got_good_dir := false;
  272.       if (new_direction_int in [ 8,1,2 ]) and (current_y <= 1) then
  273.         got_good_dir := false;
  274.       if (new_direction_int in [ 6..8 ]) and (current_x <= 1) then
  275.         got_good_dir := false;
  276.       if (new_direction_int in [ 4..6 ]) and (current_y >= max_y) then
  277.         got_good_dir := false;
  278.  
  279.     until got_good_dir;
  280.  
  281.  
  282.     if new_direction_int in [6,7,8] then
  283.       current_x := current_x - 1;
  284.  
  285.     if new_direction_int in [2,3,4] then
  286.       current_x := current_x + 1;
  287.  
  288.     if new_direction_int in [8,1,2] then
  289.       current_y := current_y - 1;
  290.  
  291.     if new_direction_int in [6,5,4] then
  292.       current_y := current_y + 1;
  293.  
  294.     if not ((current_x in [ 1..max_x ]) and (current_y in [ 1..max_y ])) then
  295.     begin
  296.       showmessage( 'Error [index(es) out of range]:' + #13 +
  297.                    'current_x = ' + inttostr( current_x ) + #13 +
  298.                    'current_y = ' + inttostr( current_y ) + #13 +
  299.                    'new_direction = ' + inttostr( new_direction_int) + #13 +
  300.                    'old_direction = ' + inttostr( old_direction_int) );
  301.       if current_x < 1 then
  302.         current_x := 1;
  303.       if current_x > max_x then
  304.         current_x := max_x;
  305.       if current_y < 1 then
  306.         current_y := 1;
  307.       if current_y > max_y then
  308.         current_y := max_y;
  309.     end
  310.     else
  311.     begin
  312.       // first land_char is '*', last is '@'
  313.       if j = max_locations then
  314.       begin
  315.         Island_2D[ current_x, current_y ].FTerrain_char := '@';
  316.         finish_x := current_x;
  317.         finish_y := current_y;
  318.         inc(location_counter);
  319.       end
  320.       else
  321.       begin
  322.         if Island_2D[ current_x, current_y ].FTerrain = Sea then
  323.         begin
  324.           inc(location_counter);
  325.           Island_2D[ current_x, current_y ].FTerrain := Land;
  326.           // land_char will be A..Z..1..9 then repeat
  327.           if char1 = '9' then
  328.             char1 := 'A'
  329.           else
  330.             char1 := chr( ord( char1 ) + 1 );
  331.             if char1 > 'Z' then
  332.               char1 := '1';
  333.           Island_2D[ current_x, current_y ].Fx := current_x;
  334.           Island_2D[ current_x, current_y ].Fy := current_y;
  335.           Island_2D[ current_x, current_y ].FTerrain_char := char1;
  336.           Island_2D[ current_x, current_y ].Flocation_number := j;
  337.           if j = 1 then
  338.             Island_2D[ current_x, current_y ].Fdescription[1] := 'The entrance to the dungeons.'
  339.           else
  340.             Island_2D[ current_x, current_y ].Fdescription[1] := 'Game location #' + inttostr(j);
  341.  
  342.           Island_2D[ current_x, current_y ].Fdescription[2] := 'Exit to the ' + directions_text[ new_direction_int ];
  343.  
  344.           if (old_x > 0) and (old_y > 0) then
  345.           begin
  346.             Island_2D[ old_x, old_y ].Fexits[ old_direction_int ] := j;
  347.             Island_2D[ current_x, current_y ].Fdescription[2] := 'Exits to the ' + directions_text[ old_direction_int ] +
  348.               ' and ' + directions_text[ new_direction_int ];
  349.           end;
  350.  
  351.           if old_direction_int <= 4 then
  352.             opposite_direction := old_direction_int + 4
  353.           else
  354.             opposite_direction := old_direction_int - 4;
  355.  
  356.           if old_direction_int = 9 then
  357.             opposite_direction := 10;
  358.  
  359.           if old_direction_int = 10 then
  360.             opposite_direction := 9;
  361.  
  362.           Island_2D[ current_x, current_y ].Fexits[ opposite_direction ] := j-1;
  363.  
  364.           old_direction_int := new_direction_int;
  365.           old_x := current_x;
  366.           old_y := current_y;
  367.         end; // then
  368.       end; // else
  369.     end; // else
  370.     inc(j);
  371.   end; // while j
  372. end; { define_island_outline }
  373. {--------------------------------------------------------------------}
  374.  
  375. procedure tform1.show_island( start_x, start_y, finish_x, finish_y, max_locations : integer);
  376. var
  377.   temp_str1 : string;
  378.   j, k : integer;
  379. begin
  380.   memo1.lines.clear;
  381.   for j := 1 to max_y do
  382.   begin
  383.     temp_str1 := '';
  384.     for k := 1 to max_x do
  385.       temp_str1 := temp_str1 + Island_2D[ k,j ].FTerrain_char;
  386.     memo1.lines.add( temp_str1 );
  387.   end;
  388.   memo1.lines.add( 'start_x = ' + inttostr(start_x) );
  389.   memo1.lines.add( 'start_y = ' + inttostr(start_y) );
  390.   memo1.lines.add( 'finish_x = ' + inttostr(finish_x) );
  391.   memo1.lines.add( 'finish_y = ' + inttostr(finish_y) );
  392.   memo1.lines.add( 'max_locations = ' + inttostr(max_locations) );
  393. end; { show_island }
  394. {--------------------------------------------------------------------}
  395.  
  396. procedure TForm1.CreateANewIslandClick(Sender: TObject);
  397. begin
  398.   make_an_island;
  399. end; { CreateaNewIslandClick }
  400. {--------------------------------------------------------------------}
  401.  
  402. procedure TForm1.OptionsClick(Sender: TObject);
  403. begin
  404.   form2 := tform2.create( nil );
  405.   try
  406.     form2.max_x.text := inttostr( max_x );
  407.     form2.max_y.text := inttostr( max_y );
  408.     if form2.showmodal = mrOK then
  409.     begin
  410.       try
  411.         max_x := strtoint( form2.max_x.text );
  412.       except
  413.         showmessage( 'Error: Value for Max_x is not a valid number.');
  414.       end;
  415.  
  416.       try
  417.         max_y := strtoint( form2.max_y.text );
  418.       except
  419.         showmessage( 'Error: Value for Max_y is not a valid number.');
  420.       end;
  421.  
  422.       if (max_x < 1) or (max_x > array_max_x) then
  423.       begin
  424.         max_x := default_max_x;
  425.         showmessage( 'Error: Max_x out of range.' );
  426.       end;
  427.  
  428.       if (max_y < 1) or (max_y > array_max_y) then
  429.       begin
  430.         max_y := default_max_y;
  431.         showmessage( 'Error: Max_y out of range.' );
  432.       end;
  433.     end;
  434.   finally
  435.     form2.close;
  436.   end;
  437. end; { SetHorizontalMaxSize1Click }
  438. {--------------------------------------------------------------------}
  439.  
  440. procedure Copy_2D_island_to_single_array;
  441. var
  442.   j,k : integer;
  443. begin
  444. //  location_counter := 0;
  445.   for j := 1 to max_x do
  446.     for k := 1 to max_y do
  447.     begin
  448.       if island_2D[ j,k ].Flocation_number > 0 then
  449.       begin
  450.         Island := TIsland.Create(
  451.            island_2D[ j,k ].FTerrain,
  452.            island_2D[ j,k ].FTerrain_char,
  453.            island_2D[ j,k ].Fdescription,
  454.            island_2D[ j,k ].Fx,
  455.            island_2D[ j,k ].Fy,
  456.            island_2D[ j,k ].Flocation_number,
  457.            island_2D[ j,k ].Fexits );
  458.         Island.Add(Island);
  459. //        inc(location_counter);
  460.       end;
  461.     end;
  462. end; { Copy_2D_island_to_single_array }
  463. {--------------------------------------------------------------------}
  464.  
  465. procedure TForm1.SaveIslandToFileClick(Sender: TObject);
  466. var
  467.   outfile : textfile;
  468.   i,j : Integer;
  469.   //temp_x, temp_y, j : integer;
  470.   k : directions;
  471.   str1 : string;
  472. begin
  473.   if savedialog1.execute then
  474.   begin
  475.     Copy_2D_island_to_single_array;
  476.     Island.Sort(compareByLocationNumber);
  477.  
  478.     AssignFile( outfile, savedialog1.filename );
  479.     Rewrite( outfile );
  480.  
  481.     Writeln( outfile, '%' );
  482.     Writeln( outfile, '% Map data' );
  483.  
  484.     for k := 0 to memo1.Lines.Count - 1 do
  485.         Writeln( outfile, memo1.Lines.Strings[ k ] );
  486.  
  487.     Writeln( outfile, '[END OF TEXT]');
  488.     Writeln( outfile, '%' );
  489.     Writeln( outfile, '% location_number');
  490.     Writeln( outfile, '% Description');
  491.     Writeln( outfile, '% [END OF TEXT]');
  492.     Writeln( outfile, '% 1..10 of ints');
  493.  
  494.     // And redisplay the list
  495.     for i := 0 to Island.Count-1 do
  496.     begin
  497.       if island.location_number > 0 then
  498.       begin
  499.         Writeln( outfile, inttostr(island.Flocation_number ));
  500.         Writeln( outfile, island.Fdescription[1] );
  501.         Writeln( outfile, island.Fdescription[2] );
  502.         Writeln( outfile, '[END OF TEXT]');
  503.         str1 := '';
  504.         for k := 1 to max_directions do
  505.           str1 := str1 + inttostr( island.Fexits[ k ] ) + ' ';
  506.         Writeln( outfile, str1 );
  507.       end;
  508.     end;
  509.  
  510.     Writeln( outfile, '* --- End of Map data' );
  511.     closeFile( outfile );
  512.  
  513.     // Free up the list
  514.     Island.free;
  515.   end;
  516. end; { SaveIslandtoFile1Click }
  517. {--------------------------------------------------------------------}
  518.  
  519. procedure tform1.make_an_island;
  520. begin
  521.   blank_island;
  522.   define_island_outline( start_x, start_y, finish_x, finish_y, location_counter, max_locations );
  523.   show_island( start_x, start_y, finish_x, finish_y, location_counter );
  524. end; { make_an_island }
  525. {--------------------------------------------------------------------}
  526.  
  527. procedure TForm1.FormCreate(Sender: TObject);
  528. var
  529.   ATerrain : Terrain_type;
  530.   ATerrain_char : char;
  531.   Adescription : description_type;
  532.   Ax, Ay, Alocation_number : integer;
  533.   Aexits : exit_type;
  534. begin
  535.   randomize;
  536.   memo1.font.Name := 'Courier New';
  537.   memo1.font.Size := 8;
  538.   max_x := default_max_x; // horizontal
  539.   max_y := default_max_y; // vertical
  540.  
  541.   // Create the TList object to hold a set of Island objects
  542.  
  543.   Island := TIsland.Create( nil, nil, nil, nil, nil, nil, nil );
  544.  
  545.   {
  546.   Island := TIsland.Create(
  547.      ATerrain : Terrain_type;
  548.      const ATerrain_char : char;
  549.      const Adescription : description_type;
  550.      const Ax, Ay, Alocation_number : integer;
  551.      const Aexits : exit_type );
  552.   }
  553.   make_an_island;
  554. end; { FormCreate }
  555. {--------------------------------------------------------------------}
  556.  
  557. initialization
  558.   {$i unit1.lrs}
  559.  
  560. end.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement