Advertisement
mixster

mixster

Jun 22nd, 2009
175
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Delphi 52.68 KB | None | 0 0
  1. program ScaRPG_Map_Editor;
  2. {
  3.     ScaRPG Map Editor
  4.     http://mixster.pastebin.com/ for the latest version
  5.     A tool that allows anyone to create an intricate map
  6.     Author for used procedures and functions is 'mixster' unless otherwise stated
  7.     Extremely heavy commenting required. If distributed without, kill whoever did it
  8.     Designed primarily for adaptation. If you don't like it, change it - that's why heavy commenting required
  9. }
  10. type
  11.   TSheet = record // Record of information on a sheet e.g. tilesheet; spritesheet.
  12.     b: Integer; // Bitmap holder for the sheet
  13.     h: Integer; // Height in tiles of sheet
  14.     w: Integer; // Width in tiles of sheet
  15.     c: TCanvas; // Holds the canvas for the bitmap 'b'
  16.   end;
  17.  
  18.   TMap = record //Record of information for the entire map
  19.     layers: array of T2DPointArray; // 3D integer array with the information for drawing the tiles. The TPoint holds the actual top left corner on the tilesheet
  20.     attribs: T2DIntArray; // layer that holds attributes for a tile
  21.     width, height, depth, sprite: Integer; // Width, height and depth for dimensions; sprite is layer where sprites are drawn on top of
  22.     vis: TIntegerArray; // Holds bitmap for visible layers
  23.     mC: TPoint; // Holds the top left position so we know which bits to draw
  24.     saved: Boolean; // Holds whether or not the map is currently saved
  25.     fp: string; // Holds filepath of where it is currently saved
  26.     fn: string; // Holds filename of map
  27.   end;
  28.  
  29. var
  30.   maps: array of TMap; // Holds the currently open maps
  31.   curMap: Integer; // Holds the index of currently visible map
  32.   curLay: Integer; // Holds the layer that everything is drawn onto
  33.   bitDeb: string; // Holds the string that has the info about all the bitmaps currently in use
  34.   bitLoc: TStringArray; // Holds the location from where the relating bitmap was created
  35.   drawSize: TPoint; // Holds the number of tiles to draw in regards to .x = width .y = height
  36.   shTile: TSheet; // See TSheet. shTile for tilesheet
  37.   drawTiles: T2DPointArray; // Holds the tiles in a box shape of which to draw
  38.   drawArea: TPoint; // Holds the position where the mouse was pressed down on the map
  39.   mode: TPoint; // Holds which method of altering and in what way
  40.   selTiles: TPointArray; // Holds the tiles that are selected when in mode x2
  41.  
  42.   fMain: TForm; // Main form for editing or creating a map
  43.   iMain: TImage; // Main visible component for displaying the map
  44.   mMain: TMainMenu; // Main menu holder thingy
  45.   mFile: TMenuItem; // Menu that holds file relating stuff
  46.   mFileOpts: array of TMenuItem; // Run of the mill menu options stored in an array
  47.   menuNames: TStringArray; // Holds the names for the options in the mFileOpts array
  48.   pSheet: TPanel; // Holds all the tilesheet relating stuff
  49.   iSheet: TImage; // Visible component for displaying the tilesheet
  50.   pTile: TPanel; // Panel to hold the currently selected tile(s) and similar stuff
  51.   iTile: TImage; // Visible component for displaying currently selected tile
  52.   bTools: array of TSpeedButton; // Buttons that switch to different map tools
  53.   toolHints: TStringArray; // Holds the hints for the buttons
  54.   tcMain: TTabControl; // Main tab bit for holding the maps
  55.   sbHorz, sbVert: TScrollBar; // Main scrollbars for scrolling map movement
  56.   sbSheet: TScrollBox; // Scrollbox that the tilesheet goes into
  57.   bLayUp, bLayDo: TButton; // Change the layer that is currently being overwritten
  58.   lCurLay: TLabel; // Holds the visual display of currently select layer
  59.   ppMain: TPopupMenu; // The main popup menu for when right clicking on the map - handles size adjustment
  60.   pmMain: array of TMenuItem; // Holds the main menu items for the popup
  61.   pmSub: array of array of TMenuItem; // Holds the submenu items for the the popup
  62.   pmOpts: array of array of array of TMenuItem; // Holds the popup menu options for adjusting map size
  63.   pmAtbs: array of TMenuItem; // Holds the popup items for setting attributes
  64.  
  65. procedure Crash(str: string); // Simple procedure to allow the script to crash easily from a single line due to laziness
  66. begin
  67.   Writeln('Crashing: ' + str); // Print the crashing reason
  68.   TerminateScript; // Stop the script running
  69. end;
  70.  
  71. function MBitmap(w, h: Integer; str, from: string): Integer; // Creation half of the memory leak debugging
  72. begin
  73.   Result := BitmapFromString(w, h, str); // Start by actually making the bitmap since we need the reference number
  74.   while (Length(bitDeb) < Result + 1) do // While the string is too short
  75.     bitDeb := bitDeb + '2'; // Increase the length of the string by adding a 2 which signifies bitmap created by alternate means
  76.   bitDeb[Result + 1] := '1'; // Set the index to 1 to signify in use - string's start at 1 not 0, hence the + 1
  77.   SetLength(bitLoc, Length(bitDeb) + 1); // This is 1 longer than the string due to it starting on 0 and me wanting them in line
  78.   bitLoc[Result + 1] := from; // Set where the bitmap was created from
  79. end;
  80.  
  81.  
  82. procedure MFreeBitmap(b: Integer); // Destroying half of the memory leak debugging
  83. begin
  84.   FreeBitmap(b); // Start by freeing it as the reference remains afterwards
  85.   b := b + 1 // Increment it to line it up with the arrays
  86.   if (b > Length(bitDeb)) then // If the debug string is too short
  87.     exit; // Then we don't need to change anything (created by LoadBitmap or something similar) so exit procedure
  88.   bitDeb[b] := '0'; // Set to 0 to signify not in use
  89.   bitLoc[b] := ''; // Set to blank
  90. end;
  91.  
  92. procedure LoadSheet(var sheet: TSheet; filename: string); // Loads a sheet from a file
  93. var
  94.   s: TPoint;
  95. begin
  96.   sheet.b := LoadBitmap(filename); // Load the bitmap
  97.   GetBitmapSize(sheet.b, s.x, s.y); // Get actual dimensions
  98.   sheet.w := s.x div 32; // Convert to tile dimenions
  99.   sheet.h := s.y div 32; // As above
  100.   sheet.c := GetBitmapCanvas(sheet.b); // Get the canvas for the bitmap
  101. end;
  102.  
  103. procedure ConvertTile(var p: TPoint; i: Integer); // Changes integer based reference to an actual position
  104. begin
  105.   p.x := (i mod shTile.w) * 32; // mod by width to get the .x then * 32 to get actual location
  106.   p.y := (i div shTile.w) * 32; // div by width to get the .y then same as above
  107. end;
  108.  
  109. function ConvertBack(p: TPoint): Integer; // Change actual location back to integer reference
  110. begin
  111.   Result := ((p.y div 32) * shTile.w) + (p.x div 32); // div both by 32 to get tile reference then .y * width and add on .x
  112. end;
  113.  
  114. procedure FreeVisibleMap(tempMap: TMap); // Frees all the drawing bitmaps
  115. var
  116.   z: Integer;
  117. begin
  118.   for z := 0 to tempMap.depth - 1 do // Loop through all the layers
  119.     MFreeBitmap(tempMap.vis[z]); // and free the bitmap used for drawing
  120. end;
  121.  
  122. procedure CreateVisibleMap(var tempMap: TMap);
  123. var
  124.   z: Integer;
  125. begin
  126.   SetLength(tempMap.vis, tempMap.depth);
  127.   for z := 0 to tempMap.depth - 1 do
  128.   begin
  129.     tempMap.vis[z] := MBitmap(drawSize.x * 32, drawSize.y * 32, '', 'CreateVisibleMap');
  130.     FastDrawClear(tempMap.vis[z], 16448505);
  131.   end;
  132. end;
  133.  
  134. procedure GenerateBlankMap(var map: TMap; w, h, d: Integer);
  135. var
  136.   z, y, x: Integer;
  137. begin
  138.   map.depth := d;
  139.   map.height := h;
  140.   map.width := w;
  141.   map.sprite := Round(h / 1.5);
  142.   map.mC := Point(0, 0);
  143.   map.fp := ScriptPath;
  144.   map.saved := true;
  145.  
  146.   SetLength(map.layers, d);
  147.   SetLength(map.vis, d);
  148.  
  149.   for z := 0 to d - 1 do
  150.   begin
  151.     SetLength(map.layers[z], h);
  152.     if (z = 0) then
  153.       SetLength(map.attribs, h);
  154.     for y := 0 to h - 1 do
  155.     begin
  156.       SetLength(map.layers[z][y], w);
  157.       if (z = 0) then
  158.         SetLength(map.attribs[y], w);
  159.       for x := 0 to w - 1 do
  160.       begin
  161.         map.layers[z][y][x] := Point(-1, 0);
  162.         if (z = 0) then
  163.           map.attribs[y][x] := 0;
  164.       end;
  165.     end;
  166.   end;
  167.   CreateVisibleMap(map);
  168. end;
  169.  
  170. procedure PaintMap; // Paints the layers on to the form so we can see the map
  171. var
  172.   t, i: Integer;
  173. begin
  174.   t := MBitmap(480, 480, '', 'PaintMap'); // Create temp bitmap
  175.   FastDrawClear(t, clBlack); // Ensure temp bitmap is blank
  176.  
  177.   for i := 0 to maps[curMap].depth - 1 do // Loop through all layers
  178.   begin
  179.     SetTransparentColor(maps[curMap].vis[i], 16448505); // Set transparent colour so the layers work nicely
  180.     FastDrawTransparent(0, 0, maps[curMap].vis[i], t); // Draw sprite layer on with transparency so bottom layer is visible
  181.   end;
  182.  
  183.   SafeDrawBitmap(t, iMain.Canvas, 0, 0); // Draw the map onto the visual component
  184.   MFreeBitmap(t); // Free our temp bitmap
  185. end;
  186.  
  187. procedure RedrawMap(tempMap: TMap); // Draw the map entirely from scratch - can be very confusing to understand, but straight forward enough
  188. var
  189.   z, y, x, t, d, blankTile: Integer;
  190.   p: TPoint;
  191.   ts, ds: TCanvas;
  192. begin
  193.   t := MBitmap(drawSize.x * 32, drawSize.y * 32, '', 'RedrawMap'); // Create temp bitmap
  194.  
  195.   ts := shTile.c; // Get the tilesheet's canvas
  196.   ds := GetBitmapCanvas(t); // Get the temp bitmap's canvas
  197.  
  198.   blankTile := MBitmap(32, 32, '', 'RedrawArea');
  199.   FastDrawClear(blankTile, 16448505);
  200.  
  201.   for z := 0 to tempMap.depth - 1 do // Loop through all the layers
  202.   begin
  203.     FastDrawClear(tempMap.vis[z], 16448505); // Ensure layer is clear
  204.     d := tempMap.vis[z]; // Set d to current layer
  205.     FastDrawClear(t, 16448505); // Clear the temp bitmap
  206.     p.y := -32; // Holds where to draw on the visible map - reset it here
  207.     for y := tempMap.mC.y to tempMap.mC.y + drawSize.y do // Loop through all on screen vertical tiles
  208.     begin
  209.       p.y := p.y + 32; // Increment the position by the height of a tile (32)
  210.  
  211.       if (y >= tempMap.height) then // If y is off the bottom of the map
  212.         break; // It won't go back on, so break the loop
  213.  
  214.       p.x := -32; // Holds where to draw on the visible map - reset it here
  215.       for x := tempMap.mC.x to tempMap.mC.x + drawSize.x do // Loop through all on screen horizontal tiles
  216.       begin
  217.         p.x := p.x + 32; // Increment the position by the width of a tile
  218.  
  219.         if (x >= tempMap.width) then // If x is off the right of the map
  220.           break; // It won't go back on, so break the loop
  221.  
  222.         if (tempMap.layers[z][y][x].x <> -1) then
  223.           SafeCopyCanvas(ts, ds, tempMap.layers[z][y][x].x, tempMap.layers[z][y][x].y, tempMap.layers[z][y][x].x + 32, tempMap.layers[z][y][x].y + 32, p.x, p.y, p.x + 32, p.y + 32) // Using the 3D TPoint array, copy the tile onto the temp bitmap using TPoint p for where to draw
  224.         else
  225.           SafeDrawBitmap(blankTile, ds, p.x, p.y);
  226.       end;
  227.     end;
  228.     FastDrawTransparent(0, 0, t, d); // Draw the layer without transparency onto appropriate bitmap
  229.   end;
  230.  
  231.   MFreeBitmap(t); // Free our temp bitmap
  232.   MFreeBitmap(blankTile);
  233. end;
  234.  
  235. procedure RepaintArea(ts, te, dp: TPoint);
  236. var
  237.   t, p, z, w, h: Integer;
  238.   tempMap: TMap;
  239. begin
  240.   tempMap := maps[curMap];
  241.   w := ((te.x + 1) - ts.x) * 32; // Get the width to draw by getting the difference in start and end then multiplying by tile width
  242.   h := ((te.y + 1) - ts.y) * 32; // As above, only height
  243.   t := MBitmap(w, h, '', 'RepaintArea');
  244.   p := MBitmap(w, h, '', 'RepaintArea');
  245.   FastDrawClear(p, clBlack);
  246.   for z := 0 to tempMap.depth - 1 do
  247.   begin
  248.     SafeCopyCanvas(GetBitmapCanvas(tempMap.vis[z]), GetBitmapCanvas(t), dp.x, dp.y, dp.x + w, dp.y + h, 0, 0, w, h);
  249.     SetTransparentColor(t, 16448505); // Set transparent colour so the layers work nicely
  250.     FastDrawTransparent(0, 0, t, p);
  251.     FastDrawClear(t, 16448505);
  252.   end;
  253.  
  254.   MFreeBitmap(t);
  255.   SafeDrawBitmap(p, iMain.Canvas, dp.x, dp.y); // Draw the map onto the visual component
  256.   MFreeBitmap(p);
  257. end;
  258.  
  259. procedure RedrawArea(ts, te, dp: TPoint); // A procedure similar to RedrawMap, but with alterations so it only redraws the set area and then draws it where told
  260. var
  261.   t, d, z, y, x, w, h, blankTile: Integer;
  262.   tc, tsc: TCanvas;
  263.   p: TPoint;
  264.   tempMap: TMap;
  265. begin
  266.   tempMap := maps[curMap];
  267.   w := (te.x - ts.x) * 32; // Get the width to draw by getting the difference in start and end then multiplying by tile width
  268.   h := (te.y - ts.y) * 32; // As above, only height
  269.   t := MBitmap(w + 32, h + 32, '', 'RedrawArea'); // Setup the bitmap used to draw the new stuff
  270.   tsc := shTile.c; // Get the tilesheet's canvas
  271.   tc := GetBitmapCanvas(t); // Get the temp bitmap's canvas
  272.   blankTile := MBitmap(32, 32, '', 'RedrawArea');
  273.   FastDrawClear(blankTile, 16448505);
  274.  
  275.   if (ts.y < 0) then
  276.     ts.y := 0;
  277.   if (te.y >= tempMap.height) then
  278.     te.y := tempMap.height - 1;
  279.  
  280.   if (ts.x < 0) then
  281.     ts.x := 0;
  282.   if (te.x >= tempMap.width) then
  283.     te.x := tempMap.width - 1;
  284.  
  285.  
  286.   for z := 0 to tempMap.depth - 1 do // Loop through all layers
  287.   begin
  288.     d := tempMap.vis[z];
  289.     FastDrawClear(t, clBlack); // Clear temp bitmap
  290.     p.y := -32; // Reset p.y
  291.     for y := ts.y to te.y do
  292.     begin
  293.       p.y := p.y + 32; // Increment position to draw to
  294.       p.x := -32;
  295.  
  296.       for x := ts.x to te.x do // Loop through all columns
  297.       begin
  298.         p.x := p.x + 32; // Increment position to draw to
  299.        
  300.         if (tempMap.layers[z][y][x].x <> -1) then
  301.           SafeCopyCanvas(tsc, tc, tempMap.layers[z][y][x].x, tempMap.layers[z][y][x].y, tempMap.layers[z][y][x].x + 32, tempMap.layers[z][y][x].y + 32, p.x, p.y, p.x + 32, p.y + 32) // Using the 3D TPoint array, copy the tile onto the temp bitmap using TPoint p for where to draw
  302.         else
  303.           SafeDrawBitmap(blankTile, tc, p.x, p.y);
  304.       end;
  305.     end;
  306.  
  307.     FastDrawTransparent(dp.x, dp.y, t, d); // Draw the layer onto appropriate bitmap without transparency for this bit
  308.   end;
  309.  
  310.   MFreeBitmap(t); // Free our temp bitmap
  311.   MFreeBitmap(blankTile);
  312.   RepaintArea(ts, te, dp);
  313. end;
  314.  
  315. procedure HandleMovement(amount: Integer; dir: Integer); // Wrapper procedure for RedrawArea
  316. var
  317.   t, z: Integer;
  318.   tc: TCanvas;
  319.   s, e, o, d: TPoint;
  320. begin
  321.   if (amount < 0) then
  322.     amount := - amount;
  323.   if (not (InRange(dir, 1, 4))) then
  324.   begin
  325.     Writeln('Invalid dir passed to HandleMovement');
  326.     exit;
  327.   end;
  328.  
  329.   t := MBitmap((drawSize.x + 1) * 32, (drawSize.y + 1) * 32, '', 'HandleMovement');
  330.   tc := GetBitmapCanvas(t);
  331.   SafeCopyCanvas(iMain.Canvas, tc, 0, 0, iMain.Width, iMain.Height, 0, 0, iMain.Width, iMain.Height);
  332.   s := maps[curMap].mC;
  333.   e := Point(s.x + drawSize.x, s.y + drawSize.y);
  334.   o := Point(0, 0);
  335.   d := Point(0, 0);
  336.   case dir of
  337.     1: begin
  338.       e.y := s.y + amount;
  339.       o.y := amount * 32;
  340.     end;
  341.     2: begin
  342.       e.x := s.x + amount;
  343.       o.x := amount * 32;
  344.     end;
  345.     3: begin
  346.       s.y := e.y - amount;
  347.       o.y := - (amount * 32);
  348.       d.y := (drawSize.y - amount) * 32;
  349.     end;
  350.     4: begin
  351.       s.x := e.x - amount;
  352.       o.x := - (amount * 32);
  353.       d.x := (drawSize.x - amount) * 32;
  354.     end;
  355.   end;
  356.  
  357.   SafeDrawBitmap(t, iMain.Canvas, o.x, o.y);
  358.   FastDrawClear(t, clBlack);
  359.  
  360.   for z := 0 to maps[curMap].depth - 1 do
  361.   begin
  362.     SafeDrawBitmap(maps[curMap].vis[z], tc, 0, 0);
  363.     SafeDrawBitmap(t, GetBitmapCanvas(maps[curMap].vis[z]), o.x, o.y);
  364.   end;
  365.  
  366.   MFreeBitmap(t);
  367.  
  368.   RedrawArea(s, e, d);
  369. end;
  370.  
  371. procedure AddMap(w, h, d: Integer); // Handles the setting up of a new map
  372. var
  373.   l: Integer;
  374. begin
  375.   l := Length(maps); // Get the length of the array
  376.   SetLength(maps, l + 1); // Increase the length of the array by 1 for the new map
  377.   GenerateBlankMap(maps[l], w, h, d); // See 'GenerateBlankMap'
  378.   tcMain.Tabs.Append('Untitled'); // Add a new tab to access it
  379.   tcMain.TabIndex := tcMain.Tabs.Count - 1; // Switch to the new tab
  380.   curMap := l; // Update curMap as well so that drawing isn't done to random maps
  381.   maps[curMap].fp := ScriptPath;
  382.   maps[curMap].fn := 'Untitled';
  383.   curLay := d - 1; // Set the drawing layer to the top layer by default
  384.   lCurLay.Caption := 'Layer ' + IntToStr(curLay) + ' selected'; // Update the layer caption so we know where we are at
  385. end;
  386.  
  387. procedure NewMap;
  388. begin
  389.   AddMap(25, 25, 3);
  390.   RedrawMap(maps[curMap]);
  391.   PaintMap;
  392. end;
  393.  
  394. procedure SetMapInfo(index: Integer; fpstr: string);
  395. begin
  396.   maps[index].saved := True; // Toggle saved to true to show that it has been saved and not modified
  397.   maps[index].fp := ExtractFilePath(fpstr); // Update the filepath to the map
  398.   maps[index].fn := ExtractFileName(fpstr); // Update the filename of the map
  399.   maps[index].fn := Copy(maps[index].fn, 1, LastPos('.', maps[curMap].fn) - 1); // And remove the file extension
  400.   tcMain.Tabs[index] := maps[index].fn; // Update the tab to show the filename minus extension
  401. end;
  402.  
  403. procedure MapImplode(var arr: TStringArray; var tempMap: TMap); // Breaks down the map into an array to be used for saving maps
  404. var
  405.   z, y, x, i: Integer;
  406. begin
  407.   SetLength(arr, 0); // Ensure array passed is empty
  408.   if ((tempMap.depth <= 0) or (tempMap.height <= 0) or (tempMap.width <= 0)) then // If map is blank/invalid size
  409.     Crash('Imploding map of invalid dimensions (one is set to zero)'); // Crash
  410.   i := 2 + (((tempMap.depth + 1) * (tempMap.height + 1)) - 1); // 2 is for the first line to hold dimension size then a blank line after. Layers held in "paragraphs", so for every layer, there should be height + 1 strings to allow for blank line underneath and then you take one away as bottom "paragraph" doesn't need blank line and 1 as added to depth for the attributes layer
  411.   SetLength(arr, i); // Set the length so it's the right length for the data
  412.   arr[0] := IntToStr(tempMap.width) + ',' + IntToStr(tempMap.height) + ',' + IntToStr(tempMap.depth); // Set first array index to hold dimenions info (x by y by z)
  413.   for x := 1 to i - 1 do // Loop through all arrays
  414.     arr[x] := ''; // Set to blank to ensure no corruption
  415.   i := 1; // Set i to 1 as 0 is used for dimensions, then 1 as a blank line - i is incremented once before being used for the first time
  416.   for z := 0 to tempMap.depth do // Loop through all layers and the attributes
  417.   begin
  418.     for y := 0 to tempMap.height - 1 do // Loop through all rows
  419.     begin
  420.       i := i + 1; // Increment i so it points to next blank array
  421.       for x := 0 to tempMap.width - 1 do // Loop through all columns
  422.         if (z < tempMap.depth) then // if it is a visual layer
  423.           arr[i] := arr[i] + IntToStr(ConvertBack(tempMap.layers[z][y][x])) + ',' // Append the converted tile and a comma to the end of the array
  424.         else // if it is the attirbutes layer
  425.           arr[i] := arr[i] + IntToStr(tempMap.attribs[y][x]) + ','; // Append the attribute and a comma to the end of the array
  426.       Delete(arr[i], Length(arr[i]), 1); // Remove the trailing comma
  427.     end;
  428.     i := i + 1; // Increment i to give the blank line between "paragraphs"
  429.   end;
  430. end;
  431.  
  432. procedure SaveMap(filename: string); // Outputs the map to a file
  433. var
  434.   f, i: Integer;
  435.   s: TStringArray;
  436. begin
  437.   f := RewriteFile(filename, False); // Open file and wipe it or create the file then assign to f
  438.   MapImplode(s, maps[curMap]); // See MapImplode
  439.   for i := 0 to High(s) do // Loop through s
  440.     WriteFileString(f, s[i] + #13 + #10); // Write to file with new line after
  441.   CloseFile(f); // Close the file
  442. end;
  443.  
  444. procedure ExportMap; // GUI method of saving a map by opening a TSaveDialog
  445. var
  446.   dialog: TSaveDialog;
  447. begin
  448.   dialog := TSaveDialog.Create(nil); // Create parentless save dialog
  449.   dialog.Filter := 'ScaRPG map|*.srpg'; // Set filter to only allow the the extension specified
  450.   dialog.InitialDir := maps[curMap].fp; // Set starting directory to same as where script is saved if not saved, else to same place if previously saved
  451.   if (not dialog.Execute) then // If no file is specified after opening the dialog and it is closed
  452.     exit; // exit procedure
  453.   SaveMap(dialog.filename); // See SaveMap - the chosen file is passed as where to save it to
  454.   SetMapInfo(curMap, dialog.filename);
  455. end;
  456.  
  457. procedure MExplode(var arr: TStringArray; str, del: string); // Standard Explode procedure written for older Scar versions
  458. var
  459.   i, h, l: Integer;
  460. begin
  461.   SetLength(arr, 0); // Ensure passed array is blank
  462.   h := -1; // Set high of array to -1
  463.   l := Length(del) - 1; // Amount to delete by based on del's length to ensure it is removed if longer than 1 char
  464.   while (true) do // Endless loop
  465.   begin
  466.     h := h + 1; // Increment h
  467.     SetLength(arr, h + 1); // Make array longer by one
  468.     i := Pos(del, str); // Find first occurence of del in str
  469.     if (i = 0) then // If it is not found
  470.       break; // Break from loop
  471.     arr[h] := Copy(str, 1, i - 1); // Copy the text until del (not including del) to the array
  472.     Delete(str, 1, i + l); // Remove copied text as well as del
  473.   end;
  474.   arr[h] := Copy(str, 1, Length(str)); // Copy remaining text into the array - del should not be on it, so no need to copy shorter than the length
  475. end;
  476.  
  477. function StrInt(s: string): Integer; // "Safe" version of IntToStr to catch dodgy map files specifically
  478. begin
  479.   try
  480.     Result := StrToInt(s); // Try standard conversion
  481.   except // If it is non-numeric
  482.     if (s = '') then // If it is blank
  483.       Result := 0 // Set to 0
  484.     else // If non-numeric and not blank
  485.       Crash('StrInt passed invalid string - "' + s + '"'); // Crash
  486.   end;
  487. end;
  488.  
  489. procedure MapExplode(var tempMap: TMap; str: string); // Creates the backend for the visual map from a string
  490. var
  491.   lineArr: TStringArray;
  492.   mapArr: array of TStringArray;
  493.   i, z, y, x: Integer;
  494. begin
  495.   if (Pos(#13 + #10, str) = 0) then // If no enter's are found in the string
  496.     Crash('Exploding map from an invalid input string'); // Crash
  497.  
  498.   MExplode(lineArr, str, #13 + #10); // See MExplode - breaks into seperate lines
  499.   SetLength(mapArr, Length(lineArr)); // Set 2D array to the same length as the number of lines
  500.   for i := 0 to High(lineArr) do // Loop through all the lines
  501.     MExplode(mapArr[i], lineArr[i], ','); //See MExplode - breaks into seperate values between the comma's
  502.    
  503.   tempMap.width := StrInt(mapArr[0][0]); // Set the dimension of the map based on the extracted values
  504.   tempMap.height := StrInt(mapArr[0][1]); // As above
  505.   tempMap.depth := StrInt(mapArr[0][2]); // As above
  506.  
  507.   i := 2; // Set i to 2 - the first array relating to the map
  508.  
  509.   // This section is like the inverse of the main loop in MapImplode
  510.   SetLength(tempMap.layers, tempMap.depth); // Set the 3D TPoint array to length of the depth
  511.   for z := 0 to tempMap.depth - 1 do // Loop through all layers and the attribute layer
  512.   begin
  513.     SetLength(tempMap.layers[z], tempMap.height); // Set a 2D TPoint array to length of the height
  514.     for y := 0 to tempMap.height - 1 do // Loop through all rows
  515.     begin
  516.       SetLength(tempMap.layers[z][y], tempMap.width); // Set the TPoint array to the length of the width
  517.       for x := 0 to tempMap.width - 1 do // Loop through all columns
  518.         ConvertTile(tempMap.layers[z][y][x], StrInt(mapArr[i][x])); // See ConvertTile - this is done so that map painting requires less calculations
  519.       i := i + 1; // Go onto the next line of the loaded "paragraph"
  520.     end;
  521.     i := i + 1; // Would be a blank line here, so go on to the next
  522.   end;
  523.  
  524.   SetLength(tempMap.attribs, tempMap.height); // Set the height of the 2DIntArray
  525.   for y := 0 to tempMap.height - 1 do // Loop through all rows
  526.   begin
  527.     SetLength(tempMap.attribs[y], tempMap.width); // Set the width of the TIntegerArray
  528.     for x := 0 to tempMap.width - 1 do // Loop through all columns
  529.       tempMap.attribs[y][x] := StrInt(mapArr[i][x]); // Convert the string into an integer attribute value
  530.     i := i + 1;
  531.   end;
  532. end;
  533.  
  534. procedure LoadMap(filename: string); // Handles the loading of a map file and doing what needs to be done
  535. var
  536.   f: Integer;
  537.   s: string;
  538. begin
  539.   if (not FileExists(filename)) then // If file does not exist
  540.     Crash('File specified to load does not exist - "' + filename + '"'); // Crash
  541.   f := OpenFile(filename, False); // Open the file specified
  542.   ReadFileString(f, s, FileSize(f)); // Read the contents of the file
  543.   CloseFile(f); // Close the file
  544.   MapExplode(maps[curMap], s); // See MapExplode - pass the file contents to break apart
  545. end;
  546.  
  547. procedure ImportMap; // GUI method of opening a map by opening a TOpemDialog
  548. var
  549.   dialog: TOpenDialog;
  550. begin
  551.   dialog := TOpenDialog.Create(nil); // Create a parentless open dialog
  552.   dialog.Filter := 'ScaRPG map|*.srpg'; // Set the filter to only show map files
  553.   if (ScriptPath <> '') then // If the script is saved
  554.     dialog.InitialDir := ScriptPath; // Set the starting directory to the place where the script is saved in
  555.   if (not dialog.Execute) then // If the dialog is launched and no file is selected when closed
  556.     exit; // exit procedure
  557.   if (not maps[curMap].saved) then // If map isn't saved
  558.     AddMap(1, 1, 1); // Then open a new tab instead
  559.   FreeVisibleMap(maps[curMap]); // Need to "refresh" the visible bitmap holders
  560.   LoadMap(dialog.filename); // See LoadMap - passing the selected file as the one to load
  561.   CreateVisibleMap(maps[curMap]); // Set up the visible layers
  562.   SetMapInfo(curMap, dialog.filename);
  563.   RedrawMap(maps[curMap]); // Redraw the map as it is unconstructed
  564.   PaintMap; // Then paint it onto the form
  565. end;
  566.  
  567. procedure CloseMap(var index: Integer);
  568. var
  569.   i: Integer;
  570. begin
  571.   FreeVisibleMap(maps[index]);
  572.   if (index < High(maps)) then
  573.   begin
  574.     for i := index to High(maps) - 1 do
  575.       Swap(maps[i], maps[i + 1]);
  576.   end
  577.   else if (index = 0) then
  578.   begin
  579.     AddMap(25, 25, 3);
  580.     curMap := 0;
  581.     Swap(maps[0], maps[1]);
  582.     RedrawMap(maps[0]);
  583.   end;
  584.   SetLength(maps, High(maps));
  585.   tcMain.Tabs.Delete(index);
  586.   if (index > High(maps)) then
  587.     index := index - 1;
  588.   PaintMap;
  589. end;
  590.  
  591. procedure PaintDrawTiles;
  592. var
  593.   t, w, h, x, y: Integer;
  594.   r: Extended;
  595. begin
  596.   h := High(drawTiles);
  597.   if (h >= 0) then
  598.     w := High(drawTiles[0])
  599.   else
  600.     w := -1;
  601.  
  602.   t := MBitmap(64, 64, '', 'PaintDrawTiles');
  603.   FastDrawClear(t, clBlack);
  604.   SafeDrawBitmap(t, iTile.Canvas, 0, 0);
  605.   MFreeBitmap(t);
  606.  
  607.   if (w = -1) then
  608.     iTile.Canvas.TextOut(16, 8, 'None')
  609.   else
  610.   begin
  611.     r := 64.0 / (Max(w, h) + 1);
  612.     for y := 0 to h do
  613.       for x := 0 to w do
  614.         if (drawTiles[y][x].x > -1) then
  615.           SafeCopyCanvas(shTile.c, iTile.Canvas, drawTiles[y][x].x, drawTiles[y][x].y, drawTiles[y][x].x + 32, drawTiles[y][x].y + 32, Round(x * r), Round(y * r), Round((x + 1) * r), Round((y + 1) * r));
  616.   end;
  617. end;
  618.  
  619. procedure MaskTile(dp: TPoint);
  620. begin
  621.   iMain.Canvas.Pen.Color := clRed;
  622.   iMain.Canvas.Brush.Style := bsFrame;
  623.   iMain.Canvas.Rectangle(dp.x + 1, dp.y + 1, dp.x + 31, dp.y + 31);
  624. end;
  625.  
  626. procedure MaskSelectedTiles;
  627. var
  628.   i, h: Integer;
  629.   p: TPoint;
  630.   ar: TBox;
  631. begin
  632.   h := Length(selTiles) - 1;
  633.   if (h = -1) then
  634.     exit;
  635.    
  636.   ar := IntToBox(maps[curMap].mC.x, maps[curMap].mC.y, maps[curMap].mC.x + drawSize.x, maps[curMap].mC.y + drawSize.y);
  637.   for i := 0 to h do
  638.     if (PointInBox(selTiles[i], ar)) then
  639.     begin
  640.       p := Point((selTiles[i].x - maps[curMap].mC.x) * 32, (selTiles[i].y - maps[curMap].mC.y) * 32);
  641.       MaskTile(p);
  642.     end;
  643. end;
  644.  
  645. procedure PaintNum(pos: TPoint; num: Integer);
  646. begin
  647.   if (num = 0) then
  648.     exit;
  649.   iMain.Canvas.TextOut(pos.x + 2, pos.y + 2, IntToStr(num));
  650. end;
  651.  
  652. procedure PaintAttributes;
  653. var
  654.   x, y: Integer;
  655. begin
  656.   for y := 0 to drawSize.y do
  657.     for x := 0 to drawSize.x do
  658.       PaintNum(Point(x * 32, y * 32), maps[curMap].attribs[maps[curMap].mC.y + y][maps[curMap].mC.x + x]);
  659. end;
  660.  
  661. procedure OnMapMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
  662. var
  663.   dp: TPoint;
  664.   h, i: Integer;
  665. begin
  666.   if (mode.y <> 1) then
  667.     exit;
  668.   if (not (ssLeft in Shift)) then
  669.     exit;
  670.  
  671.   if (mode.x = 0) then
  672.   begin
  673.     if (Length(drawTiles) = 0) then
  674.       exit
  675.     else if (Length(drawTiles[0]) = 0) then
  676.       exit;
  677.   end;
  678.  
  679.   dp := Point(Trunc(X / 32.0), Trunc(Y / 32.0));
  680.   X := dp.x + maps[curMap].mC.x;
  681.   Y := dp.y + maps[curMap].mC.y;
  682.   dp := Point(dp.x * 32, dp.y * 32);
  683.   if (X >= maps[curMap].Width) then
  684.   begin
  685.     X := maps[curMap].Width - 1;
  686.     dp.x := iMain.Width - 32;
  687.   end
  688.   else if (X < 0) then
  689.   begin
  690.     X := 0;
  691.     dp.x := 0;
  692.   end;
  693.   if (Y >= maps[curMap].Height) then
  694.   begin
  695.     Y := maps[curMap].Height - 1;
  696.     dp.y := iMain.Height - 32;
  697.   end
  698.   else if (Y < 0) then
  699.   begin
  700.     Y := 0;
  701.     dp.y := 0;
  702.   end;
  703.  
  704.   if (mode.x = 0) then
  705.   begin
  706.     if (High(drawTiles) <> -1) then
  707.       if (High(drawTiles[0]) <> -1) then
  708.        if ((maps[curMap].layers[curLay][Y][X].x = drawTiles[0][0].x) and (maps[curMap].layers[curLay][Y][X].y = drawTiles[0][0].y)) then
  709.          exit;
  710.     maps[curMap].layers[curLay][Y][X] := drawTiles[0][0];
  711.   end
  712.   else if (mode.x = 1) then
  713.   begin
  714.     if (maps[curMap].layers[curLay][Y][X].x = -1) then
  715.       exit;
  716.     maps[curMap].layers[curLay][Y][X] := Point(-1, 0)
  717.   end
  718.   else if (mode.x = 2) then
  719.   begin
  720.     h := Length(selTiles);
  721.     if ((ssCtrl in Shift) <> (PointInTPA(Point(X, Y), selTiles))) then
  722.       if (h <> 0) then
  723.         exit;
  724.  
  725.     if (not (ssCtrl in Shift)) then
  726.     begin
  727.       SetLength(selTiles, h + 1);
  728.       selTiles[h] := Point(X, Y);
  729.       MaskTile(dp);
  730.     end
  731.     else
  732.     begin
  733.       if (h = 0) then
  734.         exit;
  735.       h := h - 1;
  736.       for i := 0 to h - 1 do
  737.         if ((selTiles[i].x = X) and (selTiles[i].y = Y)) then
  738.         begin
  739.           Swap(selTiles[i], selTiles[h]);
  740.           break;
  741.         end;
  742.       SetLength(selTiles, h);
  743.       RedrawArea(Point(X, Y), Point(X , Y), dp);
  744.     end;
  745.   end;
  746.  
  747.   if ((mode.x = 0) or (mode.x = 1)) then
  748.     RedrawArea(Point(X, Y), Point(X , Y), dp);
  749. end;
  750.  
  751. procedure OnMapMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  752. begin
  753.   if ((mode.y = 0) or (mode.y = 4)) then
  754.     exit;
  755.   if ((mode.y = 1) and (Button = mbLeft)) then
  756.   begin
  757.     OnMapMouseMove(Sender, Shift, X, Y);
  758.     exit;
  759.   end;
  760.  
  761.   X := Trunc(X / 32.0);
  762.   Y := Trunc(Y / 32.0);
  763.   if (X >= maps[curMap].width) then
  764.     X := maps[curMap].width - 1
  765.   else if (X < 0) then
  766.     X := 0;
  767.   if (Y >= maps[curMap].height) then
  768.     Y := maps[curMap].height - 1
  769.   else if (Y < 0) then
  770.     Y := 0;
  771.    
  772.   drawArea := Point(X, Y);
  773.   X := (X * 32) + 16;
  774.   Y := (Y * 32) + 16;
  775.   iMain.Canvas.Pen.Color := clBlack;
  776.   iMain.Canvas.Brush.Color := clRed;
  777.   iMain.Canvas.Ellipse(X - 3, Y - 3, X + 3, Y + 3);
  778. end;
  779.  
  780. procedure OnMapMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  781. var
  782.   d, t, r: TPoint;
  783.   a, b, w, h: Integer;
  784.   s: Boolean;
  785.   sT: T2DPointArray;
  786. begin
  787.   if (mbRight = Button) then
  788.   begin
  789.     X := Trunc(X / 32.0);
  790.     Y := Trunc(Y / 32.0);
  791.     drawArea := Point(maps[curMap].mC.x + X, maps[curMap].mC.y + Y);
  792.  
  793.     GetMousePos(X, Y);
  794.     ppMain.Popup(X, Y);
  795.     exit;
  796.   end;
  797.  
  798.   if ((mode.y = 1) or (mode.y = 4)) then
  799.     exit;
  800.  
  801.   if ((curLay = maps[curMap].depth) and (mode.x <> 2)) then
  802.     exit;
  803.    
  804.   if (mode.x = 0) then
  805.   begin
  806.     if (Length(drawTiles) = 0) then
  807.       exit
  808.     else if (Length(drawTiles[0]) = 0) then
  809.       exit;
  810.   end;
  811.  
  812.   if (mode.y <> 0) then
  813.   begin
  814.     X := Trunc(X / 32.0);
  815.     Y := Trunc(Y / 32.0);
  816.     if (X >= maps[curMap].width) then
  817.       X := maps[curMap].width - 1
  818.     else if (X < 0) then
  819.       X := 0;
  820.     if (Y >= maps[curMap].height) then
  821.       Y := maps[curMap].height - 1
  822.     else if (Y < 0) then
  823.       Y := 0;
  824.     d := Point(X, Y);
  825.     if (d.x < drawArea.x) then
  826.       Swap(d.x, drawArea.x);
  827.     if (d.y < drawArea.y) then
  828.       Swap(d.y, drawArea.y);
  829.     X := maps[curMap].mC.x + drawArea.x;
  830.     Y := maps[curMap].mC.y + drawArea.y;
  831.     t := Point(d.x - drawArea.x, d.y - drawArea.y);
  832.   end
  833.   else
  834.   begin
  835.     drawArea := Point(0, 0);
  836.     d := Point(maps[curMap].width - 1, maps[curMap].height - 1);
  837.     t := d;
  838.     X := 0;
  839.     Y := 0;
  840.   end;
  841.   s := False;
  842.  
  843.   if ((mode.x = 0) and (mode.y = 3)) then
  844.   begin
  845.     h := Length(drawTiles);
  846.     if (h <> 0) then
  847.       w := Length(drawTiles[0])
  848.     else
  849.       w := 0;
  850.     if ((h < 3) or (w < 3) or (t.y < 2) or (t.x < 2)) then
  851.     begin
  852.       s := True;
  853.       mode.y := 2;
  854.     end
  855.     else
  856.     begin
  857.       h := h - 1;
  858.       w := w - 1;
  859.       SetLength(sT, h - 1);
  860.       for a := 0 to h - 2 do
  861.       begin
  862.         SetLength(sT[a], w - 1);
  863.         for b := 0 to w - 2 do
  864.           sT[a][b] := drawTiles[(a mod (h - 1)) + 1][(b mod (w - 1)) + 1];
  865.       end;
  866.  
  867.       if (drawTiles[0][0].x <> -2) then
  868.         maps[curMap].layers[curLay][Y][X] := drawTiles[0][0];
  869.       if (drawTiles[h][0].x <> -2) then
  870.         maps[curMap].layers[curLay][Y + t.y][X] := drawTiles[h][0];
  871.       if (drawTiles[0][w].x <> -2) then
  872.         maps[curMap].layers[curLay][Y][X + t.x] := drawTiles[0][w];
  873.       if (drawTiles[h][w].x <> -2) then
  874.         maps[curMap].layers[curLay][Y + t.y][X + t.x] := drawTiles[h][w];
  875.  
  876.       for a := 1 to t.y - 1 do
  877.       begin
  878.         if (drawTiles[((a - 1) mod (h - 1)) + 1][0].x <> -2) then
  879.           maps[curMap].layers[curLay][Y + a][X] := drawTiles[((a - 1) mod (h - 1)) + 1][0];
  880.         if (drawTiles[((a - 1) mod (h - 1)) + 1][w].x <> -2) then
  881.           maps[curMap].layers[curLay][Y + a][X + t.x] := drawTiles[((a - 1) mod (h - 1)) + 1][w];
  882.       end;
  883.       for b := 1 to t.x - 1 do
  884.       begin
  885.         if (drawTiles[0][((b - 1) mod (w - 1)) + 1].x <> -2) then
  886.           maps[curMap].layers[curLay][Y][X + b] := drawTiles[0][((b - 1) mod (w - 1)) + 1];
  887.         if (drawTiles[h][((b - 1) mod (w - 1)) + 1].x <> -2) then
  888.           maps[curMap].layers[curLay][Y + t.y][X + b] := drawTiles[h][((b - 1) mod (w - 1)) + 1];
  889.       end;
  890.  
  891.       for a := 1 to t.y - 1 do
  892.         for b := 1 to t.x - 1 do
  893.           if (sT[(a - 1) mod Length(sT)][(b - 1) mod Length(sT[0])].x <> -2) then
  894.             maps[curMap].layers[curLay][Y + a][X + b] := sT[(a - 1) mod Length(sT)][(b - 1) mod Length(sT[0])];
  895.     end;
  896.   end;
  897.   if ((mode.x = 0) and ((mode.y = 0) or (mode.y = 2))) then
  898.   begin
  899.     for a := 0 to t.y do
  900.       for b := 0 to t.x do
  901.         if (drawTiles[a mod Length(drawTiles)][b mod Length(drawTiles[0])].x <> -2) then
  902.           maps[curMap].layers[curLay][Y + a][X + b] := drawTiles[a mod Length(drawTiles)][b mod Length(drawTiles[0])];
  903.   end;
  904.  
  905.   if (mode.x = 2) then
  906.   begin
  907.     SetLength(selTiles, (t.x + 1) * (t.y + 1));
  908.     w := -1;
  909.     for a := 0 to t.y do
  910.       for b := 0 to t.x do
  911.       begin
  912.         w := w + 1;
  913.         selTiles[w] := Point(X + b,Y + a);
  914.       end;
  915.     Writeln(IntToStr(X) + ',' + IntToStr(Y) + ' - ' + IntToStr(X + t.x) + ',' + IntToStr(Y + t.y));
  916.   end;
  917.  
  918.   if (mode.x = 1) then
  919.   begin
  920.     for a := 0 to t.y do
  921.       for b := 0 to t.x do
  922.         maps[curMap].layers[curLay][Y + a][X + b] := Point(-1, 0);
  923.   end;
  924.  
  925.   if (s) then
  926.     mode.y := 3;
  927.  
  928.   r := Point(drawArea.x * 32, drawArea.y * 32);
  929.   if (mode.y <> 0) then
  930.     RedrawArea(Point(X, Y), Point(X + t.x, Y + t.y), r)
  931.   else
  932.   begin
  933.     RedrawMap(maps[curMap]);
  934.     PaintMap;
  935.   end;
  936.   if (mode.x = 2) then
  937.     MaskSelectedTiles;
  938. end;
  939.  
  940. procedure OnToolClick(Sender: TObject);
  941. var
  942.   i, ii, t: Integer;
  943.   ar: TBox;
  944. begin
  945.   t := -1;
  946.   for i := 0 to High(bTools) do
  947.     if (Sender = bTools[i]) then
  948.     begin
  949.       bTools[i].Down := True;
  950.       t := i;
  951.       break;
  952.     end;
  953.   if (t = -1) then
  954.     exit;
  955.   if ((mode.x = 2) and (t < 2)) then
  956.   begin
  957.     ar := IntToBox(maps[curMap].mC.x, maps[curMap].mC.y, maps[curMap].mC.x + drawSize.x, maps[curMap].mC.y + drawSize.y);
  958.     for i := 0 to High(selTiles) do
  959.       if (PointInBox(selTiles[i], ar)) then
  960.         RedrawArea(selTiles[i], selTiles[i], Point((selTiles[i].x - maps[curMap].mC.x) * 32, (selTiles[i].y - maps[curMap].mC.y) * 32));    SetLength(selTiles, 0);
  961.   end;
  962.   if (t = 7) then
  963.   begin
  964.     if (Length(selTiles) = 0) then
  965.       exit;
  966.     ar := GetTPABounds(selTiles);
  967.     ar.x2 := ar.x2 - ar.x1;
  968.     ar.y2 := ar.y2 - ar.y1;
  969.     SetLength(drawTiles, ar.y2 + 1)
  970.     for i := 0 to ar.y2 do
  971.     begin
  972.       SetLength(drawTiles[i], ar.x2 + 1);
  973.       for ii := 0 to ar.x2 do
  974.         drawTiles[i][ii] := Point(-2, 0);
  975.     end;
  976.     for i := 0 to High(selTiles) do
  977.       drawTiles[selTiles[i].y - ar.y1][selTiles[i].x - ar.x1] := maps[curMap].layers[curLay][selTiles[i].y][selTiles[i].x];
  978.     PaintDrawTiles;
  979.   end
  980.   else if (t <= 2) then
  981.     mode.x := t
  982.   else
  983.     mode.y := t - 3;
  984. end;
  985.  
  986. procedure OnMapScroll(Sender: TObject; ScrollCode: TScrollCode; var ScrollPos: Integer);
  987. var
  988.   dir, amo: Integer;
  989. begin
  990.   dir := 0;
  991.   if (Sender = sbHorz) then
  992.   begin
  993.     amo := maps[curMap].mC.x - sbHorz.Position;
  994.     maps[curMap].mC.x := sbHorz.Position;
  995.     if (amo < 0) then
  996.       dir := 4
  997.     else if (amo > 0) then
  998.       dir := 2;
  999.   end
  1000.   else if (Sender = sbVert) then
  1001.   begin
  1002.     amo := maps[curMap].mC.y - sbVert.Position;
  1003.     maps[curMap].mC.y := sbVert.Position;
  1004.     if (amo < 0) then
  1005.       dir := 3
  1006.     else if (amo > 0) then
  1007.       dir := 1;
  1008.   end;
  1009.   if (dir = 0) then
  1010.     exit;
  1011.   HandleMovement(amo, dir);
  1012. end;
  1013.  
  1014. procedure OnTilesheetMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  1015. begin
  1016.   drawArea := Point(Trunc(X / 32.0), Trunc(Y / 32.0));
  1017. end;
  1018.  
  1019. procedure OnTilesheetMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  1020. var
  1021.   w, h, a, b: Integer;
  1022. begin
  1023.   X := Trunc(X / 32.0);
  1024.   Y := Trunc(Y / 32.0);
  1025.   if (X >= shTile.w) then
  1026.     X := shTile.w - 1
  1027.   else if (X < 0) then
  1028.     X := 0;
  1029.   if (Y >= shTile.h) then
  1030.     Y := shTile.h - 1
  1031.   else if (Y < 0) then
  1032.     Y := 0;
  1033.   if (X < drawArea.x) then
  1034.     Swap(X, drawArea.x);
  1035.   if (Y < drawArea.y) then
  1036.     Swap(Y, drawArea.y);
  1037.  
  1038.   h := Y - drawArea.y;
  1039.   w := X - drawArea.x;
  1040.   SetLength(drawTiles, h + 1);
  1041.   for a := 0 to h do
  1042.   begin
  1043.     SetLength(drawTiles[a], w + 1);
  1044.     for b := 0 to w do
  1045.       drawTiles[a][b] := Point((drawArea.x + b) * 32, (drawArea.y + a) * 32);
  1046.   end;
  1047.  
  1048.   PaintDrawTiles;
  1049. end;
  1050.  
  1051. procedure OnButtonClick(Sender: TObject);
  1052. begin
  1053.   if (Sender = bLayUp) then
  1054.   begin
  1055.     curLay := curLay + 1;
  1056.     if (curLay >= maps[curMap].depth) then
  1057.       curLay := 0;
  1058.   end
  1059.   else
  1060.   begin
  1061.     curLay := curLay - 1;
  1062.     if (curLay <= -1) then
  1063.       curLay := maps[curMap].depth - 1;
  1064.   end;
  1065.  
  1066.   lCurLay.Caption := 'Layer ' + IntToStr(curLay) + ' selected'; // Update the layer caption so we know where we are drawing at
  1067. end;
  1068.  
  1069. procedure OnMenuClick(Sender: TObject);
  1070. var
  1071.   i, opt: Integer;
  1072. begin
  1073.   opt := -1;
  1074.   for i := 0 to High(mFileOpts) do
  1075.     if (Sender = mFileOpts[i]) then
  1076.     begin
  1077.       opt := i;
  1078.       break;
  1079.     end;
  1080.   if (opt = -1) then
  1081.   begin
  1082.     Writeln('I don''t know how you managed it, but you clicked on a non-existent menu item');
  1083.     exit;
  1084.   end;
  1085.   case opt of
  1086.     0: NewMap;
  1087.     1: ImportMap;
  1088.     2: ExportMap;
  1089.     3: CloseMap(curMap);
  1090.     4: begin RedrawMap(maps[curMap]); PaintMap; end;
  1091.   end;
  1092.  
  1093.   sbHorz.Position := maps[curMap].mC.x;
  1094.   sbVert.Position := maps[curMap].mC.y;
  1095. end;
  1096.  
  1097. procedure OnTabChange(Sender: TObject);
  1098. begin
  1099.   curMap := tcMain.TabIndex;
  1100.   sbHorz.Position := maps[curMap].mC.x;
  1101.   sbHorz.Max := maps[curMap].Width - drawSize.x;
  1102.   sbVert.Position := maps[curMap].mC.y;
  1103.   sbVert.Max := maps[curMap].Height - drawSize.y;
  1104.   PaintMap;
  1105. end;
  1106.  
  1107. procedure OnFormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
  1108. var
  1109.   i: Integer;
  1110.   trigs: string;
  1111. begin
  1112.   trigs := 'QWEASDZXRF0123456789';
  1113.   i := Pos(Chr(key), trigs) - 1;
  1114.   if (i = -1) then
  1115.     exit;
  1116.    
  1117.   if (i <= 7) then
  1118.     OnToolClick(bTools[i])
  1119.   else if (i = 8) then
  1120.     OnButtonClick(bLayUp)
  1121.   else if (i = 9) then
  1122.     OnButtonClick(bLayDo)
  1123.   else if (i <= 20) then
  1124.   begin
  1125.     if ((i - 10) < maps[curMap].depth) then
  1126.     begin
  1127.       curLay := i - 10;
  1128.       lCurLay.Caption := 'Layer ' + IntToStr(curLay) + ' selected'; // Update the layer caption so we know where we are drawing at
  1129.     end;
  1130.   end;
  1131. end;
  1132.  
  1133. procedure DeleteRowCol(pos: TPoint; row: Boolean);
  1134. var
  1135.   x, y, z: Integer;
  1136. begin
  1137.   if ((pos.x >= maps[curMap].width) or (pos.y >= maps[curMap].height)) then
  1138.     exit;
  1139.   for z := 0 to maps[curMap].depth - 1 do
  1140.   begin
  1141.     if (row) then
  1142.     begin
  1143.       for y := pos.y + 1 to maps[curMap].height - 1 do
  1144.         Swap(maps[curMap].layers[z][y - 1], maps[curMap].layers[z][y]);
  1145.       SetLength(maps[curMap].layers[z], maps[curMap].height - 1);
  1146.     end
  1147.     else
  1148.       for y := 0 to maps[curMap].height - 1 do
  1149.       begin
  1150.         for x := pos.x + 1 to maps[curMap].width - 1 do
  1151.           Swap(maps[curMap].layers[z][y][x - 1], maps[curMap].layers[z][y][x]);
  1152.         SetLength(maps[curMap].layers[z][y], maps[curMap].width - 1);
  1153.       end;
  1154.   end;
  1155.  
  1156.   if (row) then
  1157.     maps[curMap].height := maps[curMap].height - 1
  1158.   else
  1159.     maps[curMap].width := maps[curMap].width - 1;
  1160.  
  1161.   if (sbHorz.Position > maps[curMap].Width - drawSize.x) then
  1162.   begin
  1163.     sbHorz.Position := maps[curMap].Width - drawSize.x;
  1164.     maps[curMap].mC.x := sbHorz.Position;
  1165.   end;
  1166.  
  1167.   if (maps[curMap].Width <= drawSize.x) then
  1168.     sbHorz.Enabled := False
  1169.   else
  1170.     sbHorz.Max := maps[curMap].Width - drawSize.x;
  1171.  
  1172.   if (sbVert.Position > maps[curMap].height - drawSize.y) then
  1173.   begin
  1174.     sbVert.Position := maps[curMap].height - drawSize.y;
  1175.     maps[curMap].mC.y := sbVert.Position;
  1176.   end;
  1177.  
  1178.   if (maps[curMap].Height <= drawSize.y) then
  1179.     sbVert.Enabled := False
  1180.   else
  1181.     sbVert.Max := maps[curMap].Height - drawSize.y;
  1182. end;
  1183.  
  1184. procedure InsertRowCol(pos: TPoint; row: Boolean);
  1185. var
  1186.   x, y, z: Integer;
  1187. begin
  1188.   for z := 0 to maps[curMap].depth - 1 do
  1189.   begin
  1190.     if (row) then
  1191.     begin
  1192.       SetLength(maps[curMap].layers[z], maps[curMap].height + 1);
  1193.       SetLength(maps[curMap].layers[z][maps[curMap].height], maps[curMap].width);
  1194.       for x := 0 to maps[curMap].width - 1 do
  1195.         maps[curMap].layers[z][maps[curMap].height][x] := Point(-1, 0);
  1196.       for y := maps[curMap].height downto pos.y + 1 do
  1197.         Swap(maps[curMap].layers[z][y - 1], maps[curMap].layers[z][y]);
  1198.     end
  1199.     else
  1200.     begin
  1201.       for y := 0 to maps[curMap].height - 1 do
  1202.       begin
  1203.         SetLength(maps[curMap].layers[z][y], maps[curMap].width + 1);
  1204.         maps[curMap].layers[z][y][maps[curMap].width] := Point(-1, 0);
  1205.         for x := maps[curMap].width downto pos.x + 1 do
  1206.           Swap(maps[curMap].layers[z][y][x - 1], maps[curMap].layers[z][y][x]);
  1207.       end;
  1208.     end;
  1209.   end;
  1210.  
  1211.   if (row) then
  1212.     maps[curMap].height := maps[curMap].height + 1
  1213.   else
  1214.     maps[curMap].width := maps[curMap].width + 1;
  1215.  
  1216.   if ((not (sbHorz.Enabled)) and (maps[curMap].Width > drawSize.x)) then
  1217.     sbHorz.Enabled := True;
  1218.   if (sbHorz.Enabled) then
  1219.     sbHorz.Max := maps[curMap].Width - drawSize.x;
  1220.  
  1221.   if ((not (sbVert.Enabled)) and (maps[curMap].Height > drawSize.y)) then
  1222.     sbVert.Enabled := True;
  1223.   if (sbVert.Enabled) then
  1224.     sbVert.Max := maps[curMap].Height - drawSize.y;
  1225. end;
  1226.  
  1227. procedure OnPopupClick(Sender: TObject);
  1228. var
  1229.   a, b, c: Integer;
  1230.   f: Boolean;
  1231.   s: string;
  1232. begin
  1233.   f := False;
  1234.   for a := 0 to Length(pmOpts) - 1 do
  1235.     if (f) then
  1236.       break
  1237.     else
  1238.       for b := 0 to Length(pmOpts[a]) - 1 do
  1239.         if (f) then
  1240.           break
  1241.         else
  1242.           for c := 0 to Length(pmOpts[a][b]) - 1 do
  1243.             if (Sender = pmOpts[a][b][c]) then
  1244.             begin
  1245.               f := True;
  1246.               break;
  1247.             end;
  1248.   if (not f) then
  1249.   begin
  1250.     for a := 0 to Length(pmAtbs) -1 do
  1251.       if (Sender = pmAtbs[a]) then
  1252.       begin
  1253.         f := True;
  1254.         break;
  1255.       end;
  1256.     if (not f) then
  1257.     begin
  1258.       Writeln('Invalid menu item pointing to OnPopupClick');
  1259.       exit;
  1260.     end;
  1261.     if (a <> 0) then
  1262.       a := a + 1;
  1263.      
  1264.     c := Length(selTiles) - 1;
  1265.     if (c = -1) then
  1266.       exit;
  1267.     for b := 0 to Length(selTiles) - 1 do
  1268.       maps[curMap].attribs[selTiles[b].y][selTiles[b].x] := a;
  1269.  
  1270.     PaintAttributes;
  1271.     exit;
  1272.   end;
  1273.  
  1274.   a := a - 1;
  1275.   b := b - 1;
  1276.   if (a = 1) then
  1277.   begin
  1278.     if ((c = 0) or (c = 1) or (c = 3)) then
  1279.     begin
  1280.       if (c = 1) then
  1281.         drawArea := Point(drawArea.x - 1, drawArea.y - 1)
  1282.       else if (c = 3) then
  1283.         drawArea := Point(drawArea.x + 1, drawArea.y + 1);
  1284.       DeleteRowCol(drawArea, b = 0);
  1285.     end
  1286.     else
  1287.     begin
  1288.       a := -1
  1289.       while a = -1 do
  1290.       begin
  1291.         s := Readln('How many to delete');
  1292.         if (s = '') then
  1293.           exit;
  1294.         a := StrToIntDef(s, -1);
  1295.       end;
  1296.       if (a <= 0) then
  1297.         exit;
  1298.        
  1299.       if (c = 2) then
  1300.       begin
  1301.         if ((b = 0) and (drawArea.y < a)) then
  1302.           a := drawArea.y
  1303.         else if ((b = 1) and (drawArea.x < a)) then
  1304.           a := drawArea.x;
  1305.         drawArea := Point(drawArea.x - a, drawArea.y - a);
  1306.       end
  1307.       else
  1308.         drawArea := Point(drawArea.x + 1, drawArea.y + 1);
  1309.  
  1310.       for c := 1 to a do
  1311.         DeleteRowCol(Point(drawArea.x, drawArea.y), b = 0);
  1312.     end;
  1313.   end
  1314.   else
  1315.   begin
  1316.     if ((c = 0) or (c = 2)) then
  1317.     begin
  1318.       if (c = 2) then
  1319.         drawArea := Point(drawArea.x + 1, drawArea.y + 1);
  1320.       InsertRowCol(drawArea, b = 0);
  1321.     end
  1322.     else
  1323.     begin
  1324.       a := -1
  1325.       while a = -1 do
  1326.       begin
  1327.         s := Readln('How many to insert');
  1328.         if (s = '') then
  1329.           exit;
  1330.         a := StrToIntDef(s, -1);
  1331.       end;
  1332.       if (a <= 0) then
  1333.         exit;
  1334.       if (c = 3) then
  1335.         drawArea := Point(drawArea.x + 1, drawArea.y + 1);
  1336.       for c := 1 to a do
  1337.         InsertRowCol(Point(drawArea.x, drawArea.y), b = 0);
  1338.   end;
  1339.   end;
  1340.   RedrawMap(maps[curMap]);
  1341.   PaintMap;
  1342. end;
  1343.  
  1344. procedure ApplyGrid(var img: TImage; col: Integer);
  1345. var
  1346.   x, y, xl, yl, tc: Integer;
  1347. begin
  1348.   xl := img.Width div 32;
  1349.   yl := img.Height div 32;
  1350.   tc := img.Canvas.Pen.Color;
  1351.   img.Canvas.Pen.Color := col;
  1352.   if (yl > 2) then
  1353.     for y := 1 to yl - 1 do
  1354.     begin
  1355.       img.Canvas.MoveTo(0, y * 32);
  1356.       img.Canvas.LineTo(img.Width, y * 32);
  1357.     end;
  1358.   if (xl > 2) then
  1359.     for x := 1 to xl - 1 do
  1360.     begin
  1361.       img.Canvas.MoveTo(x * 32, 0);
  1362.       img.Canvas.LineTo(x * 32, img.Height);
  1363.     end;
  1364.   img.Canvas.Pen.Color := tc;
  1365. end;
  1366.  
  1367. // Standard form setup procedure - no comments
  1368. procedure SetupForm;
  1369. var
  1370.   i, ii, iii: Integer;
  1371.   popMode, popDir, popAtb: TStringArray;
  1372.   popAmount: array of TStringArray;
  1373. begin
  1374.   fMain := CreateForm;
  1375.  
  1376.   mMain := TMainMenu.Create(fMain);
  1377.   mFile := TMenuItem.Create(fMain);
  1378.   mFIle.Caption := 'File';
  1379.   menuNames := ['New', 'Open', 'Save', 'Close', 'Refresh'];
  1380.   SetLength(mFileOpts, Length(menuNames));
  1381.  
  1382.   for i := 0 to High(mFileOpts) do
  1383.   begin
  1384.     mFileOpts[i] := TMenuItem.Create(fMain);
  1385.     mFileOpts[i].Caption := menuNames[i];
  1386.     mFileOpts[i].OnClick := @OnMenuClick;
  1387.     mFIle.Add(mFileOpts[i]);
  1388.   end;
  1389.  
  1390.   mMain.Items.Add(mFile);
  1391.  
  1392.   with fMain do
  1393.   begin
  1394.     Caption := 'ScaRPG - Map Editor';
  1395.     ClientWidth := ((drawSize.x + 1) * 32) + ((shTile.w + 1) * 32) + 32;
  1396.     ClientHeight := ((drawSize.y + 1) * 32) + 32;
  1397.     Position := poDesktopCenter;
  1398.     OnKeyUp := @OnFormKeyUp;
  1399.     KeyPreview := True;
  1400.   end;
  1401.  
  1402.   pSheet := TPanel.Create(fMain);
  1403.   with pSheet do
  1404.   begin
  1405.     Parent := fMain;
  1406.     Width := (shTile.w + 1) * 32;
  1407.     Height := fMain.ClientHeight;
  1408.     Left := fMain.ClientWidth - Width;
  1409.     Top := 0;
  1410.   end;
  1411.  
  1412.   sbSheet := TScrollBox.Create(pSheet);
  1413.   with sbSheet do
  1414.   begin
  1415.     Parent := pSheet;
  1416.     Align := alClient;
  1417.     VertScrollBar.Increment := 32;
  1418.   end;
  1419.  
  1420.   iSheet := TImage.Create(sbSheet);
  1421.   with iSheet do
  1422.   begin
  1423.     Parent := sbSheet;
  1424.     Left := 0;
  1425.     Top := 0;
  1426.     Width := (shTile.w * 32);
  1427.     Height := (shTile.h * 32);
  1428.     SafeDrawBitmap(shTile.b, Canvas, 0, 0);
  1429.     OnMouseDown := @OnTilesheetMouseDown;
  1430.     OnMouseUp := @OnTilesheetMouseUp;
  1431.     ApplyGrid(iSheet, clRed);
  1432.   end;
  1433.  
  1434.   pTile := TPanel.Create(pSheet);
  1435.   with pTile do
  1436.   begin
  1437.     Parent := pSheet;
  1438.     Align := alBottom;
  1439.     Height := 96;
  1440.   end;
  1441.  
  1442.   iTile := TImage.Create(pTile);
  1443.   with iTile do
  1444.   begin
  1445.     Parent := pTile;
  1446.     Left := 16;
  1447.     Top := 16;
  1448.     Width := 64;
  1449.     Height := 64;
  1450.     PaintDrawTiles;
  1451.   end;
  1452.  
  1453.   bLayUp := TButton.Create(pTile);
  1454.   with bLayUp do
  1455.   begin
  1456.     Parent := pTile;
  1457.     Left := 96;
  1458.     Top := 16;
  1459.     Width := 32;
  1460.     Height := 32;
  1461.     Caption := '^';
  1462.     OnClick := @OnButtonClick;
  1463.   end;
  1464.  
  1465.   bLayDo := TButton.Create(pTile);
  1466.   with bLayDo do
  1467.   begin
  1468.     Parent := pTile;
  1469.     Left := 96;
  1470.     Top := 48;
  1471.     Width := 32;
  1472.     Height := 32;
  1473.     Caption := 'v';
  1474.     OnClick := @OnButtonClick;
  1475.   end;
  1476.  
  1477.   toolHints := ['Draw', 'Erase', 'Select', 'Flood', 'Pencil', 'Tile', 'Box', 'Copy'];
  1478.   SetLength(bTools, Length(toolHints));
  1479.   for i := 0 to High(toolHints) do
  1480.   begin
  1481.     bTools[i] := TSpeedButton.Create(pTile);
  1482.     with bTools[i] do
  1483.     begin
  1484.       Parent := pTile;
  1485.       Left := 144 + ((i mod 3) * 32);
  1486.       Top := 16 + ((i div 3) * 21);
  1487.       Width := 32;
  1488.       Height := 21;
  1489.       Hint := toolHints[i];
  1490.       Caption := Copy(toolHints[i], 1, 4);
  1491.       OnClick := @OnToolClick;
  1492.       AllowAllUp := True;
  1493.       if (i < 3) then
  1494.         GroupIndex := 1
  1495.       else
  1496.         GroupIndex := 2;
  1497.     end;
  1498.   end;
  1499.  
  1500.   bTools[0].Down := True;
  1501.   bTools[4].Down := True;
  1502.  
  1503.   lCurLay := TLabel.Create(pTile);
  1504.   with lCurLay do
  1505.   begin
  1506.     Parent := pTile;
  1507.     Left := 16;
  1508.     Top := 0;
  1509.     Caption := 'Layer ' + IntToStr(curLay) + ' selected';
  1510.   end;
  1511.  
  1512.   tcMain := TTabControl.Create(fMain);
  1513.   with tcMain do
  1514.   begin
  1515.     Parent := fMain;
  1516.     Left := 0;
  1517.     Top := 0;
  1518.     Width := (drawSize.x * 32) + 48;
  1519.     Height := (drawSize.y * 32) + 64;
  1520.     AddMap(20, 20, 3);
  1521.     OnChange := @OnTabChange;
  1522.   end;
  1523.  
  1524.   iMain := TImage.Create(tcMain);
  1525.   with iMain do
  1526.   begin
  1527.     Parent := tcMain;
  1528.     Left := 16;
  1529.     Top := 32;
  1530.     Width := drawSize.x * 32;
  1531.     Height := drawSize.y * 32;
  1532.     OnMouseDown := @OnMapMouseDown;
  1533.     OnMouseUp := @OnMapMouseUp;
  1534.     OnMouseMove := @OnMapMouseMove;
  1535.     ApplyGrid(iMain, clRed);
  1536.   end;
  1537.  
  1538.   sbHorz := TScrollBar.Create(tcMain);
  1539.   with sbHorz do
  1540.   begin
  1541.     Parent := tcMain;
  1542.     Left := 16;
  1543.     Top := 32 + iMain.Height;
  1544.     Width := iMain.Width;
  1545.     Height := 16;
  1546.     Kind := sbHorizontal;
  1547.     Max := maps[curMap].Width - drawSize.x;
  1548.     OnScroll := @OnMapScroll;
  1549.   end;
  1550.  
  1551.   sbVert := TScrollBar.Create(tcMain);
  1552.   with sbVert do
  1553.   begin
  1554.     Parent := tcMain;
  1555.     Left := 16 + iMain.Width;
  1556.     Top := 32;
  1557.     Width := iMain.Width;
  1558.     Height := 16;
  1559.     Kind := sbVertical;
  1560.     Max := maps[curMap].Height - drawSize.y;
  1561.     OnScroll := @OnMapScroll;
  1562.   end;
  1563.  
  1564.   ppMain := TPopupMenu.Create(fMain);
  1565.  
  1566.   popMode := ['Insert', 'Delete', 'Attribute', 'Cancel'];
  1567.   SetLength(pmMain, Length(popMode));
  1568.   for i := 0 to Length(popMode) - 1 do
  1569.   begin
  1570.     pmMain[i] := TMenuItem.Create(fMain);
  1571.     pmMain[i].Caption := popMode[i];
  1572.   end;
  1573.  
  1574.   popDir := ['Row', 'Column'];
  1575.   SetLength(pmSub, Length(popDir));
  1576.   for i := 0 to Length(pmSub) - 1 do
  1577.   begin
  1578.     SetLength(pmSub[i], Length(popDir));
  1579.     for ii := 0 to Length(pmSub[i]) - 1 do
  1580.     begin
  1581.       pmSub[i][ii] := TMenuItem.Create(fMain);
  1582.       pmSub[i][ii].Caption := popDir[ii];
  1583.     end;
  1584.   end;
  1585.  
  1586.   SetLength(popAmount, Length(pmSub));
  1587.   for i := 0 to Length(popAmount) - 1 do
  1588.     case i of
  1589.       0: popAmount[i] := ['1 Before', 'x Before', '1 After', 'x After'];
  1590.       1: popAmount[i] := ['Current', 'Previous', 'Previous x', 'Next', 'Next x'];
  1591.     end;
  1592.  
  1593.   SetLength(pmOpts, Length(pmSub));
  1594.   for i := 0 to Length(pmOpts) - 1 do
  1595.   begin
  1596.     SetLength(pmOpts[i], Length(pmSub[i]));
  1597.     for ii := 0 to Length(pmOpts[i]) - 1 do
  1598.     begin
  1599.       SetLength(pmOpts[i][ii], Length(popAmount[i]));
  1600.       for iii := 0 to Length(pmOpts[i][ii]) - 1 do
  1601.       begin
  1602.         pmOpts[i][ii][iii] := TMenuItem.Create(fMain);
  1603.         pmOpts[i][ii][iii].Caption := popAmount[i][iii];
  1604.         pmOpts[i][ii][iii].OnClick := @OnPopupClick;
  1605.         pmSub[i][ii].Add(pmOpts[i][ii][iii]);
  1606.       end;
  1607.     end;
  1608.   end;
  1609.  
  1610.   for i := 0 to Length(pmSub) - 1 do
  1611.     for ii := 0 to Length(pmSub[i]) - 1 do
  1612.       pmMain[i].Add(pmSub[i][ii]);
  1613.      
  1614.   popAtb := ['Ground (d)', 'Wall', 'Block NPC', 'Water', 'Hole'];
  1615.  
  1616.   SetLength(pmAtbs, Length(popAtb));
  1617.   for i := 0 to Length(pmAtbs) - 1 do
  1618.   begin
  1619.     pmAtbs[i] := TMenuItem.Create(fMain);
  1620.     pmAtbs[i].Caption := popAtb[i];
  1621.     pmAtbs[i].OnClick := @OnPopupClick;
  1622.     pmMain[2].Add(pmAtbs[i]);
  1623.   end;
  1624.      
  1625.   for i := 0 to Length(popMode) - 1 do
  1626.     ppMain.Items.Add(pmMain[i]);
  1627.  
  1628.   RedrawMap(maps[curMap]);
  1629.   PaintMap;
  1630.   fMain.ShowModal;
  1631. end;
  1632.  
  1633. var
  1634.   v: TVariantArray;
  1635.   m, z, i: Integer;
  1636. // Main loop - no comments
  1637. begin
  1638.   Writeln('begin');
  1639.   drawSize := Point(15, 15);
  1640.   mode := Point(0, 1);
  1641.   LoadSheet(shTile, ScriptPath + 'Tiles.bmp');
  1642.  
  1643.   ThreadSafeCall('SetupForm', v);
  1644. //  SetupForm; // Use without SafeCall for debugging unknown errors, though very prone to self destruct when done so - use at own risk
  1645.   FreeForm(fMain);
  1646.  
  1647.   for m := 0 to High(maps) do
  1648.     for z := 0 to maps[m].depth - 1 do
  1649.       MFreeBitmap(maps[m].vis[z]);
  1650.   FreeBitmap(shTile.b);
  1651.  
  1652.   // Section for debugging memory leaks - see MBitmap and MFreeBitmap
  1653.   // Don't rely on it for fixing all leaks - only works when you use MBitmap and not LoadBitmap etc.
  1654.   while True do
  1655.   begin
  1656.     i := Pos('1', bitDeb);
  1657.     if (i = 0) then
  1658.       Break;
  1659.     Writeln('Bitmap created from ''' + bitLoc[i] + ''' not freed!');
  1660.     MFreeBitmap(i - 1);
  1661.   end;
  1662.  
  1663.   Writeln('end');
  1664. end.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement