Advertisement
petethepoet

Unit1.pas (3) of dtm_maker

May 6th, 2011
138
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Pascal 17.23 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, fgl;
  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.   Tmap_detail = Object
  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.   Generic TIsland<T> = class
  98.       Items : array of Tmap_detail;
  99.     public
  100.     constructor Create(AList: TList);
  101.     procedure Add( Value : Tmap_detail );
  102.   end;
  103.  
  104.   island_2D_type = array[ 1..array_max_x, 1..array_max_y ] of
  105.                      Tmap_detail;
  106.  
  107. {
  108.   island_single_type = array[ 1..(array_max_x * array_max_y) ] of
  109.                          map_detail;
  110. }
  111.  
  112.   // The form class definition
  113.  
  114.   { TForm1 }
  115.  
  116.   TForm1 = class(TForm)
  117.     File1: TMenuItem;
  118.     MainMenu1: TMainMenu;
  119.     CreateANewIsland: TMenuItem;
  120.     Memo1: TMemo;
  121.     Options: TMenuItem;
  122.     SaveIslandToFile: TMenuItem;
  123.     SaveDialog1: TSaveDialog;
  124.     procedure CreateANewIslandClick(Sender: TObject);
  125.     procedure OptionsClick(Sender: TObject);
  126.     procedure SaveIslandToFileClick(Sender: TObject);
  127.     procedure show_island( start_x, start_y, finish_x, finish_y, max_locations : integer);
  128.     procedure make_an_island;
  129.     procedure FormCreate(Sender: TObject);
  130.   private
  131.     { Private declarations }
  132.     // The TList object we use in this code
  133.  
  134.     // Method to show the contents of our list object
  135.     procedure ShowListContents;
  136.  
  137.     { Public declarations }
  138.   public
  139.  
  140.   end;
  141.  
  142. var
  143.   Form1: TForm1;
  144.   Island : TIsland;
  145.   Island_2D : island_2D_type;
  146.   max_x, max_y,
  147.   location_counter,
  148.   max_locations,
  149.   start_x, start_y,
  150.   finish_x, finish_y : integer;
  151.  
  152. implementation
  153.  
  154. uses set_max;
  155.  
  156. {$R *.lfm}
  157.  
  158. procedure TIsland.Add(Value: T);
  159. begin
  160.   SetLength(Items, Length(Items) + 1);
  161.   Items[Length(Items) - 1] := Value;
  162. end;
  163. {--------------------------------------------------------------------}
  164.  
  165. procedure TIsland.Create(AList : TList);
  166. begin
  167.   inherited Create;
  168.   self.FMap_detail := AList;
  169. end
  170. {--------------------------------------------------------------------}
  171.  
  172. // TList sort routine : compare Islands by name
  173. // --------------------------------------------------------------------------
  174. // The returned integer has the following value :
  175. //
  176. //   > 0 : (positive)   Item1 is less than Item2
  177. //     0 : Item1 is equal to Item2
  178. //   < 0 : (negative)   Item1 is greater than Item2
  179. function compareByLocationNumber(Item1 : Pointer; Item2 : Pointer) : Integer;
  180. var
  181.   Island1, Island2 : TIsland;
  182. begin
  183.   // We start by viewing the object pointers as TIsland objects
  184.   Island1 := TIsland(Item1);
  185.   Island2 := TIsland(Item2);
  186.  
  187.   // Now compare by string
  188.   if Island1.Flocation_number > Island2.Flocation_number then
  189.     Result := 1
  190.   else
  191.     if Island1.Flocation_number = Island2.Flocation_number then
  192.       Result := 0
  193.     else
  194.       Result := -1;
  195. end;
  196. {--------------------------------------------------------------------}
  197.  
  198. procedure blank_island;
  199. var
  200.   j,k,d : integer;
  201. begin
  202.   for j := 1 to max_x do
  203.     for k := 1 to max_y do
  204.     begin
  205.       Island_2D[ j,k ].FTerrain := Sea;
  206.       Island_2D[ j,k ].FTerrain_char := '.';
  207.       Island_2D[ j,k ].Fdescription[1] := '';
  208.       Island_2D[ j,k ].Fdescription[2] := '';
  209.       Island_2D[ j,k ].Fx := 0;
  210.       Island_2D[ j,k ].Fy := 0;
  211.       Island_2D[ j,k ].Flocation_number := 0;
  212.       for d := 1 to max_directions do
  213.         Island_2D[ j,k ].Fexits[ d ] := 0;
  214.     end;
  215.   max_locations := trunc( max_x * max_y / 1.575 ); // gives 2000 for 35 x 90
  216. end; { blank_island }
  217. {--------------------------------------------------------------------}
  218.  
  219. {
  220.           UP
  221.           9
  222.       NW  N  NE
  223.         8 1 2
  224.       W 7   3 E
  225.         6 5 4
  226.       SW  S  SE
  227.           10
  228.          DOWN
  229. }
  230.  
  231. procedure define_island_outline( var start_x, start_y,
  232.   finish_x, finish_y: integer;
  233.   var location_counter, max_locations : integer );
  234. var
  235.   got_good_dir : boolean;
  236.   old_x, old_y,
  237.   j,
  238.   current_x, current_y : integer;
  239.   new_direction_int,
  240.   old_direction_int,
  241.   opposite_direction : directions;
  242.   char1 : char;
  243.   temp_direction : directions;
  244. begin
  245.   current_x := random( max_x ) + 1;
  246.   current_y := random( max_y ) + 1;
  247.   start_x := current_x;
  248.   start_y := current_y;
  249.   location_counter := 1;
  250.   old_x := 0;
  251.   old_y := 0;
  252.   old_direction_int := 1; //north
  253.   temp_direction := 1; //north
  254.   char1 := '9'; // this will mean that the sequence starts with an "A"
  255.   Island_2D[ current_x, current_y ].FTerrain := Land;
  256.   Island_2D[ current_x, current_y ].FTerrain_char := '*';
  257.   max_locations := trunc( max_x * max_y / 1.575 ); // gives 2000 for 35 x 90
  258.  
  259.   j := 1;
  260.   while (j <= max_locations) do
  261.   begin
  262.     repeat
  263. {
  264.           UP
  265.           9
  266.       NW  N  NE
  267.         8 1 2
  268.       W 7   3 E
  269.         6 5 4
  270.       SW  S  SE
  271.           10
  272.          DOWN
  273. }
  274.       new_direction_int := random( max_directions ) + 1;
  275.       got_good_dir := true;
  276.  
  277.       // we don't want to go straight back where we came from
  278.       if abs(new_direction_int - old_direction_int) = 4 then
  279.         got_good_dir := false;
  280.  
  281.       if ((old_direction_int = 9) and (new_direction_int = 10)) or
  282.          ((old_direction_int = 10) and (new_direction_int = 9)) then
  283.         got_good_dir := false;
  284.  
  285.       // next 4 tests are to keep from going outside the bounds of the map.
  286.       if (new_direction_int in [ 2..4 ]) and (current_x >= max_x) then
  287.         got_good_dir := false;
  288.       if (new_direction_int in [ 8,1,2 ]) and (current_y <= 1) then
  289.         got_good_dir := false;
  290.       if (new_direction_int in [ 6..8 ]) and (current_x <= 1) then
  291.         got_good_dir := false;
  292.       if (new_direction_int in [ 4..6 ]) and (current_y >= max_y) then
  293.         got_good_dir := false;
  294.  
  295.     until got_good_dir;
  296.  
  297.  
  298.     if new_direction_int in [6,7,8] then
  299.       current_x := current_x - 1;
  300.  
  301.     if new_direction_int in [2,3,4] then
  302.       current_x := current_x + 1;
  303.  
  304.     if new_direction_int in [8,1,2] then
  305.       current_y := current_y - 1;
  306.  
  307.     if new_direction_int in [6,5,4] then
  308.       current_y := current_y + 1;
  309.  
  310.     if not ((current_x in [ 1..max_x ]) and (current_y in [ 1..max_y ])) then
  311.     begin
  312.       showmessage( 'Error [index(es) out of range]:' + #13 +
  313.                    'current_x = ' + inttostr( current_x ) + #13 +
  314.                    'current_y = ' + inttostr( current_y ) + #13 +
  315.                    'new_direction = ' + inttostr( new_direction_int) + #13 +
  316.                    'old_direction = ' + inttostr( old_direction_int) );
  317.       if current_x < 1 then
  318.         current_x := 1;
  319.       if current_x > max_x then
  320.         current_x := max_x;
  321.       if current_y < 1 then
  322.         current_y := 1;
  323.       if current_y > max_y then
  324.         current_y := max_y;
  325.     end
  326.     else
  327.     begin
  328.       // first land_char is '*', last is '@'
  329.       if j = max_locations then
  330.       begin
  331.         Island_2D[ current_x, current_y ].FTerrain_char := '@';
  332.         finish_x := current_x;
  333.         finish_y := current_y;
  334.         inc(location_counter);
  335.       end
  336.       else
  337.       begin
  338.         if Island_2D[ current_x, current_y ].FTerrain = Sea then
  339.         begin
  340.           inc(location_counter);
  341.           Island_2D[ current_x, current_y ].FTerrain := Land;
  342.           // land_char will be A..Z..1..9 then repeat
  343.           if char1 = '9' then
  344.             char1 := 'A'
  345.           else
  346.             char1 := chr( ord( char1 ) + 1 );
  347.             if char1 > 'Z' then
  348.               char1 := '1';
  349.           Island_2D[ current_x, current_y ].Fx := current_x;
  350.           Island_2D[ current_x, current_y ].Fy := current_y;
  351.           Island_2D[ current_x, current_y ].FTerrain_char := char1;
  352.           Island_2D[ current_x, current_y ].Flocation_number := j;
  353.           if j = 1 then
  354.             Island_2D[ current_x, current_y ].Fdescription[1] := 'The entrance to the dungeons.'
  355.           else
  356.             Island_2D[ current_x, current_y ].Fdescription[1] := 'Game location #' + inttostr(j);
  357.  
  358.           Island_2D[ current_x, current_y ].Fdescription[2] := 'Exit to the ' + directions_text[ new_direction_int ];
  359.  
  360.           if (old_x > 0) and (old_y > 0) then
  361.           begin
  362.             Island_2D[ old_x, old_y ].Fexits[ old_direction_int ] := j;
  363.             Island_2D[ current_x, current_y ].Fdescription[2] := 'Exits to the ' + directions_text[ old_direction_int ] +
  364.               ' and ' + directions_text[ new_direction_int ];
  365.           end;
  366.  
  367.           if old_direction_int <= 4 then
  368.             opposite_direction := old_direction_int + 4
  369.           else
  370.             opposite_direction := old_direction_int - 4;
  371.  
  372.           if old_direction_int = 9 then
  373.             opposite_direction := 10;
  374.  
  375.           if old_direction_int = 10 then
  376.             opposite_direction := 9;
  377.  
  378.           Island_2D[ current_x, current_y ].Fexits[ opposite_direction ] := j-1;
  379.  
  380.           old_direction_int := new_direction_int;
  381.           old_x := current_x;
  382.           old_y := current_y;
  383.         end; // then
  384.       end; // else
  385.     end; // else
  386.     inc(j);
  387.   end; // while j
  388. end; { define_island_outline }
  389. {--------------------------------------------------------------------}
  390.  
  391. procedure tform1.show_island( start_x, start_y, finish_x, finish_y, max_locations : integer);
  392. var
  393.   temp_str1 : string;
  394.   j, k : integer;
  395. begin
  396.   memo1.lines.clear;
  397.   for j := 1 to max_y do
  398.   begin
  399.     temp_str1 := '';
  400.     for k := 1 to max_x do
  401.       temp_str1 := temp_str1 + Island_2D[ k,j ].FTerrain_char;
  402.     memo1.lines.add( temp_str1 );
  403.   end;
  404.   memo1.lines.add( 'start_x = ' + inttostr(start_x) );
  405.   memo1.lines.add( 'start_y = ' + inttostr(start_y) );
  406.   memo1.lines.add( 'finish_x = ' + inttostr(finish_x) );
  407.   memo1.lines.add( 'finish_y = ' + inttostr(finish_y) );
  408.   memo1.lines.add( 'max_locations = ' + inttostr(max_locations) );
  409. end; { show_island }
  410. {--------------------------------------------------------------------}
  411.  
  412. procedure TForm1.CreateANewIslandClick(Sender: TObject);
  413. begin
  414.   make_an_island;
  415. end; { CreateaNewIslandClick }
  416. {--------------------------------------------------------------------}
  417.  
  418. procedure TForm1.OptionsClick(Sender: TObject);
  419. begin
  420.   form2 := tform2.create( nil );
  421.   try
  422.     form2.max_x.text := inttostr( max_x );
  423.     form2.max_y.text := inttostr( max_y );
  424.     if form2.showmodal = mrOK then
  425.     begin
  426.       try
  427.         max_x := strtoint( form2.max_x.text );
  428.       except
  429.         showmessage( 'Error: Value for Max_x is not a valid number.');
  430.       end;
  431.  
  432.       try
  433.         max_y := strtoint( form2.max_y.text );
  434.       except
  435.         showmessage( 'Error: Value for Max_y is not a valid number.');
  436.       end;
  437.  
  438.       if (max_x < 1) or (max_x > array_max_x) then
  439.       begin
  440.         max_x := default_max_x;
  441.         showmessage( 'Error: Max_x out of range.' );
  442.       end;
  443.  
  444.       if (max_y < 1) or (max_y > array_max_y) then
  445.       begin
  446.         max_y := default_max_y;
  447.         showmessage( 'Error: Max_y out of range.' );
  448.       end;
  449.     end;
  450.   finally
  451.     form2.close;
  452.   end;
  453. end; { SetHorizontalMaxSize1Click }
  454. {--------------------------------------------------------------------}
  455.  
  456. procedure Copy_2D_island_to_single_array;
  457. var
  458.   j,k : integer;
  459. begin
  460. //  location_counter := 0;
  461.   for j := 1 to max_x do
  462.     for k := 1 to max_y do
  463.     begin
  464.       if island_2D[ j,k ].Flocation_number > 0 then
  465.       begin
  466.         Island := TIsland.Create(
  467.            island_2D[ j,k ].FTerrain,
  468.            island_2D[ j,k ].FTerrain_char,
  469.            island_2D[ j,k ].Fdescription,
  470.            island_2D[ j,k ].Fx,
  471.            island_2D[ j,k ].Fy,
  472.            island_2D[ j,k ].Flocation_number,
  473.            island_2D[ j,k ].Fexits );
  474.         Island.Add(Island);
  475. //        inc(location_counter);
  476.       end;
  477.     end;
  478. end; { Copy_2D_island_to_single_array }
  479. {--------------------------------------------------------------------}
  480.  
  481. procedure TForm1.SaveIslandToFileClick(Sender: TObject);
  482. var
  483.   outfile : textfile;
  484.   i,j : Integer;
  485.   //temp_x, temp_y, j : integer;
  486.   k : directions;
  487.   str1 : string;
  488. begin
  489.   if savedialog1.execute then
  490.   begin
  491.     Copy_2D_island_to_single_array;
  492.     Island.Sort(compareByLocationNumber);
  493.  
  494.     AssignFile( outfile, savedialog1.filename );
  495.     Rewrite( outfile );
  496.  
  497.     Writeln( outfile, '%' );
  498.     Writeln( outfile, '% Map data' );
  499.  
  500.     for k := 0 to memo1.Lines.Count - 1 do
  501.         Writeln( outfile, memo1.Lines.Strings[ k ] );
  502.  
  503.     Writeln( outfile, '[END OF TEXT]');
  504.     Writeln( outfile, '%' );
  505.     Writeln( outfile, '% location_number');
  506.     Writeln( outfile, '% Description');
  507.     Writeln( outfile, '% [END OF TEXT]');
  508.     Writeln( outfile, '% 1..10 of ints');
  509.  
  510.     // And redisplay the list
  511.     for i := 0 to Island.Count-1 do
  512.     begin
  513.       if island.location_number > 0 then
  514.       begin
  515.         Writeln( outfile, inttostr(island.Flocation_number ));
  516.         Writeln( outfile, island.Fdescription[1] );
  517.         Writeln( outfile, island.Fdescription[2] );
  518.         Writeln( outfile, '[END OF TEXT]');
  519.         str1 := '';
  520.         for k := 1 to max_directions do
  521.           str1 := str1 + inttostr( island.Fexits[ k ] ) + ' ';
  522.         Writeln( outfile, str1 );
  523.       end;
  524.     end;
  525.  
  526.     Writeln( outfile, '* --- End of Map data' );
  527.     closeFile( outfile );
  528.  
  529.     // Free up the list
  530.     Island.free;
  531.   end;
  532. end; { SaveIslandtoFile1Click }
  533. {--------------------------------------------------------------------}
  534.  
  535. procedure tform1.make_an_island;
  536. begin
  537.   blank_island;
  538.   define_island_outline( start_x, start_y, finish_x, finish_y, location_counter, max_locations );
  539.   show_island( start_x, start_y, finish_x, finish_y, location_counter );
  540. end; { make_an_island }
  541. {--------------------------------------------------------------------}
  542.  
  543. procedure TForm1.FormCreate(Sender: TObject);
  544. var
  545.   ATerrain : Terrain_type;
  546.   ATerrain_char : char;
  547.   Adescription : description_type;
  548.   Ax, Ay, Alocation_number : integer;
  549.   Aexits : exit_type;
  550. begin
  551.   randomize;
  552.   memo1.font.Name := 'Courier New';
  553.   memo1.font.Size := 8;
  554.   max_x := default_max_x; // horizontal
  555.   max_y := default_max_y; // vertical
  556.  
  557.   // Create the TList object to hold a set of Island objects
  558.  
  559.   Island := TIsland.Create( nil, nil, nil, nil, nil, nil, nil );
  560.  
  561.   {
  562.   Island := TIsland.Create(
  563.      ATerrain : Terrain_type;
  564.      const ATerrain_char : char;
  565.      const Adescription : description_type;
  566.      const Ax, Ay, Alocation_number : integer;
  567.      const Aexits : exit_type );
  568.   }
  569.   make_an_island;
  570. end; { FormCreate }
  571. {--------------------------------------------------------------------}
  572.  
  573. initialization
  574.   {$i unit1.lrs}
  575.  
  576. end.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement