Subversion Repositories jumper

Rev

Rev 8 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. unit LevelFunctions;
  2.  
  3. interface
  4.  
  5. uses
  6.   SysUtils, Dialogs, Functions, ExtCtrls, Graphics;
  7.  
  8. type
  9.   TFieldType = (ftLocked, ftLockedWithTab, ftEmpty, ftGreen, ftYellow, ftRed);
  10.  
  11.   TFieldProperties = record
  12.     Typ: TFieldType;
  13.     Goal: Boolean;
  14.   end;
  15.  
  16.   TLevelType = (ltStandard, ltDiagonal, ltError);
  17.  
  18.   TLevelArray = array of array of TFieldProperties;
  19.  
  20.   TLevelError = (leNone, leInvalidElement, leNoIndicator, leMultipleIndicator,
  21.                  leLevelIncomplete, leHeaderError, leInvalidGoal);
  22.  
  23. procedure DrawLevelPreview(LevelString: string; Image: TImage; BackgroundColor: TColor);
  24. function GetLevelType(LevelString: string): TLevelType;
  25. function CheckLevelIntegrity(LevelString: string; ShowErrors: boolean): TLevelError; overload;
  26. function CheckLevelIntegrity(LevelString: string): TLevelError; overload;
  27. function LevelStringToLevelArray(LevelString: string; ShowErrors: boolean): TLevelArray;
  28.  
  29. var
  30.   AllowDiagonalMoves: boolean;
  31.  
  32. implementation
  33.  
  34. const
  35.   PREVIEW_BLOCK_SIZE = 10; // Enthält Field und Abstand
  36.   PREVIEW_TAB_SIZE = PREVIEW_BLOCK_SIZE div 2; // 5
  37.   HEADER_SIZE = 3;
  38.   ERED = '3';
  39.   EYEL = '2';
  40.   EGRE = '1';
  41.   ELOC = 'X';
  42.   EEMP = 'E';
  43.   EIND = '!';
  44.   ETAR = '>';
  45.   ESPE = '*';
  46.   TY_DIA = 'D';
  47.   TY_NOR = 'N';
  48.  
  49. resourcestring
  50.   LNG_LVL_INVALID_ELEMENT = 'Level invalid: There are invalid elements in the file.'+#13#10#13#10+'Valid elements are "1", "2", "3", "X", "*", "E" and ">" as goal prefix.';
  51.   LNG_LVL_INVALID_WIDTH = 'Level invalid: No width indicator ("!") found.';
  52.   LNG_LVL_INVALID_HEIGHT_MUTLIPLE = 'Level invalid: The level''s actual length is not a multiple of the width.';
  53.   LNG_LVL_INVALID_MULTIPLE_WIND = 'Level invalid: There are multiple width indicators ("!").';
  54.   LNG_LVL_INVALID_HEADER = 'Level invalid: The header is invalid. It does not match the structure "1(D|N)~".';
  55.   LNG_INVALID_GOAL = 'Level invalid: A goal does not point to a valid accessable element ("3", "2", "1" or "E").';
  56.  
  57. procedure DrawLevelPreview(LevelString: string; Image: TImage; BackgroundColor: TColor);
  58. var
  59.   LevelArray: TLevelArray;
  60.   i, j: integer;
  61.   t: TFieldType;
  62.   halftabs: integer;
  63. begin
  64.   LevelArray := nil;
  65.  
  66.   ClearImage(Image, BackgroundColor);
  67.  
  68.   LevelArray := LevelStringToLevelArray(LevelString, false);
  69.  
  70.   for i := Low(LevelArray) to High(LevelArray) do
  71.   begin
  72.     halftabs := 0;
  73.     for j := Low(LevelArray[i]) to High(LevelArray[i]) do
  74.     begin
  75.       t := LevelArray[i][j].Typ;
  76.  
  77.       case t of
  78.         ftLocked: Image.Canvas.Brush.Color := BackgroundColor;
  79.         ftLockedWithTab: begin
  80.           Image.Canvas.Brush.Color := BackgroundColor;
  81.           inc(halftabs);
  82.         end;
  83.         ftEmpty: Image.Canvas.Brush.Color := clWhite;
  84.         ftGreen: Image.Canvas.Brush.Color := clLime;
  85.         ftYellow: Image.Canvas.Brush.Color := clYellow;
  86.         ftRed: Image.Canvas.Brush.Color := clRed;
  87.       end;
  88.  
  89.       if LevelArray[i][j].Goal then
  90.         Image.Canvas.Pen.Color := clBlack
  91.       else
  92.         Image.Canvas.Pen.Color := BackgroundColor;
  93.  
  94.       Image.Canvas.Rectangle(j*PREVIEW_BLOCK_SIZE - halftabs*PREVIEW_TAB_SIZE, i*PREVIEW_BLOCK_SIZE, j*PREVIEW_BLOCK_SIZE - halftabs*PREVIEW_TAB_SIZE+PREVIEW_BLOCK_SIZE, i*PREVIEW_BLOCK_SIZE+PREVIEW_BLOCK_SIZE);
  95.     end;
  96.   end;
  97. end;
  98.  
  99. function GetLevelType(LevelString: string): TLevelType;
  100. begin
  101.   if CheckLevelIntegrity(LevelString) = leNone then
  102.   begin
  103.     if Copy(LevelString, 2, 1) = TY_DIA then
  104.     begin
  105.       result := ltDiagonal;
  106.     end
  107.     else // if Copy(LevelString, 2, 1) = TY_NOR
  108.     begin
  109.       result := ltStandard;
  110.     end;
  111.   end
  112.   else
  113.   begin
  114.     result := ltError;
  115.   end;
  116. end;
  117.  
  118. procedure ShowErrorMessage(error: TLevelError);
  119. begin
  120.   case error of
  121.     leNone: ;
  122.     leInvalidElement: ShowMessage(LNG_LVL_INVALID_ELEMENT);
  123.     leNoIndicator: ShowMessage(LNG_LVL_INVALID_WIDTH);
  124.     leMultipleIndicator: ShowMessage(LNG_LVL_INVALID_MULTIPLE_WIND);
  125.     leLevelIncomplete: ShowMessage(LNG_LVL_INVALID_HEIGHT_MUTLIPLE);
  126.     leHeaderError: ShowMessage(LNG_LVL_INVALID_HEADER);
  127.     leInvalidGoal: ShowMessage(LNG_INVALID_GOAL);
  128.   end;
  129. end;
  130.  
  131. function CheckLevelIntegrity(LevelString: string; ShowErrors: boolean): TLevelError;
  132. begin
  133.   result := CheckLevelIntegrity(LevelString);
  134.   if ShowErrors then ShowErrorMessage(result);
  135. end;
  136.  
  137. function CheckLevelIntegrity(LevelString: string): TLevelError;
  138. var
  139.   W: integer;
  140.   H: extended;
  141.   header, h_ver, h_dia, h_del, tmp: string;
  142.   p: integer;
  143. begin
  144.   result := leNone;
  145.  
  146.   // Entfernt die Zeilenumbrüche
  147.  
  148.   LevelString := RemoveLineBreaks(LevelString);
  149.  
  150.   // Check 1: Ist der Header OK?
  151.  
  152.   header := copy(LevelString, 1, HEADER_SIZE);
  153.  
  154.   h_ver := copy(header, 1, 1);
  155.   if h_ver <> '1' then
  156.   begin
  157.     result := leHeaderError;
  158.     Exit;
  159.   end;
  160.  
  161.   h_dia := copy(header, 2, 1);
  162.   if (h_dia <> TY_DIA) and (h_dia <> TY_NOR) then
  163.   begin
  164.     result := leHeaderError;
  165.     Exit;
  166.   end;
  167.  
  168.   h_del := copy(header, 3, 1);
  169.   if h_del <> '~' then
  170.   begin
  171.     result := leHeaderError;
  172.     Exit;
  173.   end;
  174.  
  175.   LevelString := copy(LevelString, HEADER_SIZE+1, Length(LevelString)-HEADER_SIZE);
  176.  
  177.   // Check 2: Steht das ggf. vorhandenen ">" vor einem gültigen Feld 1, 2, 3, E?
  178.  
  179.   p := Position(LevelString, ETAR);
  180.  
  181.   while (p <> -1) do
  182.   begin
  183.     tmp := copy(LevelString, p+1, 1);
  184.  
  185.     if (tmp <> EEMP) and (tmp <> EGRE) and (tmp <> EYEL) and (tmp <> ERED) then
  186.     begin
  187.       result := leInvalidGoal;
  188.       Exit;
  189.     end;
  190.  
  191.     LevelString := StringReplace(LevelString, ETAR, '', []); // Dieses Ziel entfernen
  192.  
  193.     p := Position(LevelString, ETAR);
  194.   end;
  195.  
  196.   // Check 3: Kommt überhaupt ein "!" vor?
  197.  
  198.   W := Position(LevelString, EIND);
  199.  
  200.   if W = -1 then
  201.   begin
  202.     result := leNoIndicator;
  203.     Exit;
  204.   end;
  205.  
  206.   // Check 4: Kam das "!" mehrmals vor?
  207.  
  208.   LevelString := StringReplace(LevelString, EIND, '', []); // Das Erste entfernen
  209.  
  210.   if Position(LevelString, EIND) <> -1 then // gibt es ein Zweites?
  211.   begin
  212.     result := leMultipleIndicator;
  213.     Exit;
  214.   end;
  215.  
  216.   // Check 5: Geht das Level nicht in einem Quadrat oder Rechteck auf?
  217.  
  218.   H := (Length(LevelString) - 1) / W;
  219.  
  220.   if not Ganzzahlig(H) then
  221.   begin
  222.     result := leLevelIncomplete;
  223.     Exit;
  224.   end;
  225.  
  226.   // Check 6: Gibt es ungültige Elemente im LevelString?
  227.  
  228.   LevelString := StringReplace(LevelString, ESPE, '', [rfReplaceAll]);
  229.   LevelString := StringReplace(LevelString, ELOC, '', [rfReplaceAll]);
  230.   LevelString := StringReplace(LevelString, EEMP, '', [rfReplaceAll]);
  231.   LevelString := StringReplace(LevelString, EGRE, '', [rfReplaceAll]);
  232.   LevelString := StringReplace(LevelString, EYEL, '', [rfReplaceAll]);
  233.   LevelString := StringReplace(LevelString, ERED, '', [rfReplaceAll]);
  234.  
  235.   if Length(LevelString) > 0 then
  236.   begin
  237.     result := leInvalidElement;
  238.     Exit;
  239.   end;
  240.  
  241.   // Check 7: Kann im Level gesprungen werden
  242.  
  243.   { Wird hier nicht abgeprüft, da dafür zuerst der PlayGround gebaut sein muss.
  244.     Es ist außerdem eher ein logischer Fehler, kein Fehler in der Levelstruktur! }
  245. end;
  246.  
  247. function LevelStringToLevelArray(LevelString: string; ShowErrors: boolean): TLevelArray;
  248. var
  249.   i, j, j_dec, c: integer;
  250.   m: string;
  251.   t: TFieldType;
  252.   W, H: integer;
  253.   err: TLevelError;
  254.   NextIsGoal: boolean;
  255. begin
  256.   // Zuerst nach Fehlern suchen
  257.   err := CheckLevelIntegrity(LevelString, ShowErrors);
  258.   if err <> leNone then exit;
  259.  
  260.   // Headerinformationen auslesen
  261.   AllowDiagonalMoves := copy(LevelString, 2, 1) = TY_DIA;
  262.  
  263.   // Header entfernen
  264.   LevelString := copy(LevelString, HEADER_SIZE+1, Length(LevelString)-HEADER_SIZE);
  265.  
  266.   // Entfernt die Zeilenumbrüche
  267.   LevelString := RemoveLineBreaks(LevelString);
  268.  
  269.   // Dimensionen abmessen
  270.   W := Position(StringReplace(LevelString, ETAR, '', [rfReplaceAll]), EIND) - 1;
  271.   LevelString := StringReplace(LevelString, EIND, '', [rfReplaceAll]);
  272.   H := Length(LevelString) div W;
  273.  
  274.   c := 1;
  275.   NextIsGoal := false;
  276.  
  277.   SetLength(result, round(H));
  278.   for i := Low(result) to High(result) do
  279.   begin
  280.     j_dec := 0;
  281.     SetLength(result[i], round(W));
  282.     for j := Low(result[i]) to High(result[i])+1 do  // +1 wegen dem möglichen zusätzlichem ">"
  283.     begin
  284.       if (j = High(result[i])+1) and (j_dec = 0) then break;
  285.       m := Copy(LevelString, c, 1);
  286.       if m = ETAR then
  287.       begin
  288.         NextIsGoal := true;
  289.         inc(j_dec);
  290.       end
  291.       else
  292.       begin
  293.              if m = EEMP then t := ftEmpty
  294.         else if m = EGRE then t := ftGreen
  295.         else if m = EYEL then t := ftYellow
  296.         else if m = ERED then t := ftRed
  297.         else if m = ESPE then t := ftLockedWithTab
  298.         else t := ftLocked;
  299.         result[i][j-j_dec].Typ := t;
  300.         result[i][j-j_dec].Goal := NextIsGoal;
  301.         if NextIsGoal then NextIsGoal := false;
  302.       end;
  303.       inc(c);
  304.     end;
  305.   end;
  306. end;
  307.  
  308. end.
  309.