Advertisement
petethepoet

unit1.pas of dtm_maker project

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