Subversion Repositories jumper

Rev

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