Subversion Repositories spacemission

Rev

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

  1. unit ComLevelReader;
  2.  
  3. interface
  4.  
  5. const
  6.   NumEnemyTypes = 7;
  7.  
  8. type
  9.   TEnemyType = (
  10.     etUnknown,
  11.     etEnemyAttacker,
  12.     etEnemyAttacker2,
  13.     etEnemyAttacker3,
  14.     etEnemyMeteor,
  15.     etEnemyUFO,
  16.     etEnemyUFO2,
  17.     etEnemyBoss
  18.   );
  19.  
  20.   TEnemyAdvent = record
  21.     enemyType: TEnemyType;
  22.     x: integer;
  23.     y: integer;
  24.     lifes: integer;
  25.   end;
  26.  
  27.   TLevelData = class(TObject)
  28.   strict private
  29.     procedure SortEnemies;
  30.   public
  31.     LevelEditorLength: integer;
  32.     EnemyAdventTable: array of TEnemyAdvent;
  33.     function IndexOfEnemy(x,y:integer;enemyType:TEnemyType;lifes:integer): integer;
  34.     procedure AddEnemy(x,y:integer;enemyType:TEnemyType;lifes:integer);
  35.     procedure DeleteEnemy(i: integer); overload;
  36.     procedure DeleteEnemy(x,y:integer;enemyType:TEnemyType;lifes:integer); overload;
  37.     function CountEnemies: integer;
  38.     function HasBoss: boolean;
  39.     procedure Clear;
  40.     procedure Load(filename: string);
  41.     procedure Save(filename: string);
  42.   end;
  43.  
  44. function GetLevelFileName(lev: integer): string;
  45.  
  46. implementation
  47.  
  48. uses
  49.   SysUtils, StrUtils, Classes, Global;
  50.  
  51. const
  52.   DefaultLevelLength = 1200;
  53.  
  54. function GetLevelFileName(lev: integer): string;
  55. begin
  56.   result := OwnDirectory+'Levels\Level '+inttostr(lev)+'.lev'; // Version 0.3+ Level Files
  57.   if not FileExists(Result) then
  58.     result := OwnDirectory+'Levels\Lev'+inttostr(lev)+'A1.lev'; // Version 0.2 Level Files
  59. end;
  60.  
  61. { TLevelData }
  62.  
  63. procedure TLevelData.Clear;
  64. begin
  65.   SetLength(EnemyAdventTable, 0);
  66.   LevelEditorLength := DefaultLevelLength;
  67. end;
  68.  
  69. function TLevelData.CountEnemies: integer;
  70. begin
  71.   result := Length(EnemyAdventTable);
  72. end;
  73.  
  74. procedure TLevelData.DeleteEnemy(i: integer);
  75. var
  76.   j: integer;
  77. begin
  78.   for j := i+1 to CountEnemies-1 do
  79.   begin
  80.     EnemyAdventTable[j-1] := EnemyAdventTable[j];
  81.   end;
  82.   SetLength(EnemyAdventTable, Length(EnemyAdventTable)-1);
  83. end;
  84.  
  85. procedure TLevelData.DeleteEnemy(x, y: integer; enemyType: TEnemyType;
  86.   lifes: integer);
  87. begin
  88.   DeleteEnemy(IndexOfEnemy(x, y, enemyType, lifes));
  89. end;
  90.  
  91. function TLevelData.HasBoss: boolean;
  92. var
  93.   i: integer;
  94. begin
  95.   for i := 0 to Length(EnemyAdventTable) - 1 do
  96.   begin
  97.     if EnemyAdventTable[i].enemyType = etEnemyBoss then
  98.     begin
  99.       result := true;
  100.       exit;
  101.     end;
  102.   end;
  103.   result := false;
  104. end;
  105.  
  106. procedure TLevelData.AddEnemy(x,y:integer;enemyType:TEnemyType;lifes:integer);
  107. begin
  108.   SetLength(EnemyAdventTable, Length(EnemyAdventTable)+1);
  109.  
  110.   if enemyType = etEnemyMeteor then lifes := 0;
  111.   if x mod RasterW <> 0 then raise Exception.CreateFmt('X-Koordinate muss ohne Rest durch %d teilbar sein', [RasterW]);
  112.   if y mod RasterH <> 0 then raise Exception.CreateFmt('Y-Koordinate muss ohne Rest durch %d teilbar sein', [RasterH]);
  113.  
  114.   EnemyAdventTable[Length(EnemyAdventTable)-1].x         := x;
  115.   EnemyAdventTable[Length(EnemyAdventTable)-1].y         := y;
  116.   EnemyAdventTable[Length(EnemyAdventTable)-1].enemyType := enemyType;
  117.   EnemyAdventTable[Length(EnemyAdventTable)-1].lifes     := lifes;
  118. end;
  119.  
  120. function TLevelData.IndexOfEnemy(x, y: integer; enemyType: TEnemyType;
  121.   lifes: integer): integer;
  122. var
  123.   i: integer;
  124. begin
  125.   for i := 0 to Length(EnemyAdventTable) - 1 do
  126.   begin
  127.     if (EnemyAdventTable[i].x = x) and
  128.        (EnemyAdventTable[i].y = y) and
  129.        (EnemyAdventTable[i].enemyType = enemyType) and
  130.        (EnemyAdventTable[i].lifes = lifes) then
  131.     begin
  132.       result := i;
  133.       exit;
  134.     end;
  135.   end;
  136.   result := -1;
  137. end;
  138.  
  139. procedure TLevelData.Load(filename: string);
  140. var
  141.   sl, sl2: TStringList;
  142.   curline: integer;
  143.   ergebnis: string;
  144.   z, act: integer;
  145.   i, j: integer;
  146.   temp: string;
  147.   m: array[1..6] of tstrings;
  148.   tmpX, tmpY, tmpLifes: integer;
  149.   tmpEnemy: TEnemyType;
  150.   tmpRest: string;
  151. begin
  152.   Clear;
  153.  
  154.   sl := TStringList.Create;
  155.   try
  156.     if EndsText('A1.lev', filename) then
  157.     begin
  158.       {$REGION 'Backwards compatibility level format 0.2 (split into 5-6 files; convert to 0.3)'}
  159.       m[1] := TStringList.create;
  160.       m[2] := TStringList.create;
  161.       m[3] := TStringList.create;
  162.       m[4] := TStringList.create;
  163.       m[5] := TStringList.create;
  164.       m[6] := TStringList.create;
  165.       try
  166.         for i := 1 to 6 do
  167.         begin
  168.           filename[Length(filename)-4] := IntToStr(i)[1]; // ...A2.sav, ...A3.sav, etc.
  169.           if FileExists(filename) then
  170.             m[i].loadfromfile(filename);
  171.         end;
  172.         m[1].strings[0] := '-624';
  173.         if m[6].Text = '' then m[6].Text := '30000';
  174.  
  175.         sl.Add('; SpaceMission 0.3');
  176.         sl.Add(temp);
  177.         for j := 0 to m[1].count-2 do
  178.         begin
  179.           for i := 0 to m[1].count-2 do
  180.           begin
  181.             if strtoint(m[1].strings[i]) > strtoint(m[1].strings[i+1]) then
  182.             begin
  183.               m[1].exchange(i, i+1);
  184.               m[2].exchange(i, i+1);
  185.               m[3].exchange(i, i+1);
  186.               m[4].exchange(i, i+1);
  187.               m[5].exchange(i, i+1);
  188.             end;
  189.           end;
  190.         end;
  191.         for i := 0 to m[3].count-1 do
  192.         begin
  193.           for j := 1 to 4 do
  194.           begin
  195.             if j = 1 then sl.Add(m[3].strings[i]);
  196.             if j = 2 then sl.Add(m[1].strings[i]);
  197.             if j = 3 then sl.Add(m[2].strings[i]);
  198.             if j = 4 then sl.Add(m[4].strings[i]);
  199.           end;
  200.         end;
  201.       finally
  202.         FreeAndNil(m[1]);
  203.         FreeAndNil(m[2]);
  204.         FreeAndNil(m[3]);
  205.         FreeAndNil(m[4]);
  206.         FreeAndNil(m[5]);
  207.         FreeAndNil(m[6]);
  208.       end;
  209.       {$ENDREGION}
  210.     end
  211.     else
  212.     begin
  213.       sl.LoadFromFile(filename);
  214.     end;
  215.  
  216.     if sl.Strings[0] = '; SpaceMission 0.3' then
  217.     begin
  218.       {$REGION 'Backwards compatibility level format 0.3 (convert to 0.4)'}
  219.       sl.Strings[0] := '; SpaceMission 0.4';
  220.       sl.Insert(1, '; LEV-File');
  221.       {$ENDREGION}
  222.     end;
  223.  
  224.     if (sl.Strings[0] = '; SpaceMission 0.4') and
  225.        (sl.Strings[1] = '; LEV-File') then
  226.     begin
  227.       {$REGION 'Backwards compatibility level format 0.4 (convert to 1.0)'}
  228.       sl2 := TStringList.Create;
  229.       try
  230.         z := 0;
  231.         act := 0;
  232.         while z < sl.Count do
  233.         begin
  234.           inc(z);
  235.           if z > 2 then inc(act);
  236.           if act = 5 then act := 1;
  237.           ergebnis := sl.Strings[z-1];
  238.           if ergebnis = '; SpaceMission 0.4' then
  239.             sl2.Add('; SpaceMission 1.0')
  240.           else
  241.           begin
  242.             if (ergebnis = '30000') and (z = 3) then
  243.               sl2.Add(IntTostr(DefaultLevelLength))
  244.             else
  245.             begin
  246.               //if not (((ergebnis = '0') and (z = 4)) or ((ergebnis = '-624') and (z = 5)) or ((ergebnis = '222') and (z = 6)) or ((ergebnis = '3') and (z = 7))) then
  247.               if (z < 4) or (z > 7) then
  248.               begin
  249.                 if act = 4 then
  250.                   sl2.Add(inttostr(strtoint(ergebnis) + 32 - (5 * (strtoint(ergebnis) div 37))))
  251.                 else
  252.                   sl2.Add(Ergebnis);
  253.               end;
  254.             end;
  255.           end;
  256.         end;
  257.         sl.Text := sl2.Text;
  258.       finally
  259.         FreeAndNil(sl2);
  260.       end;
  261.       {$ENDREGION}
  262.     end;
  263.  
  264.     if (sl.Strings[0] = '; SpaceMission 1.0') and
  265.        (sl.Strings[1] = '; LEV-File') then
  266.     begin
  267.       {$REGION 'Level format 1.0'}
  268.       LevelEditorLength := StrToInt(sl.Strings[2]);
  269.       curline := 3;
  270.       while curline < sl.Count do
  271.       begin
  272.         tmpEnemy := TEnemyType(strtoint(sl.Strings[curline]));
  273.         Inc(curline);
  274.         tmpX := strtoint(sl.Strings[curline]);
  275.         Inc(curline);
  276.         tmpY := strtoint(sl.Strings[curline]);
  277.         Inc(curline);
  278.         tmpLifes := strtoint(sl.Strings[curline]);
  279.         Inc(curline);
  280.         AddEnemy(tmpX, tmpY, tmpEnemy, tmpLifes);
  281.       end;
  282.       {$ENDREGION}
  283.     end
  284.     else if (sl.Strings[0] = '[SpaceMission Level, Format 1.2]') then
  285.     begin
  286.       {$REGION 'Level format 1.2'}
  287.       LevelEditorLength := DefaultLevelLength;
  288.       for curline := 1 to sl.Count-1 do
  289.       begin
  290.         // 1234567890123456789012345678901234567890
  291.         // 123456 123456 123456 123456 123456 ; Kommentar
  292.         if (sl.Strings[curline].Trim = '') or
  293.            (Copy(sl.Strings[curline], 1, 1) = ';') then
  294.           // Do nothing
  295.         else if Copy(sl.Strings[curline], 1, 6).TrimRight = 'Width' then
  296.         begin
  297.           LevelEditorLength := StrToInt(TrimRight(Copy(sl.Strings[curline], 8, 6)))
  298.         end
  299.         else if Copy(sl.Strings[curline], 1, 6).TrimRight = 'Enemy' then
  300.         begin
  301.           tmpEnemy := TEnemyType(strtoint(TrimRight(Copy(sl.Strings[curline], 8, 6))));
  302.           tmpX     := strtoint(TrimRight(Copy(sl.Strings[curline], 15, 6)));
  303.           tmpY     := strtoint(TrimRight(Copy(sl.Strings[curline], 22, 6)));
  304.           tmpLifes := strtoint(TrimRight(Copy(sl.Strings[curline], 29, 6)));
  305.           tmpRest  := Copy(sl.Strings[curline], 36, Length(sl.Strings[curline])-36+1);
  306.           if (Copy(tmpRest, 1, 1) <> ';') and (Trim(tmpRest) <> '') then
  307.             raise Exception.CreateFmt('Zeile %d ist ungültig (Zusatzinfo am Ende)', [curline+1]);
  308.           AddEnemy(tmpX, tmpY, tmpEnemy, tmpLifes);
  309.         end
  310.         else
  311.         begin
  312.           raise Exception.CreateFmt('Zeile %d ist ungültig (Unbekannter Zeilentyp)', [curline+1]);
  313.         end;
  314.       end;
  315.       {$ENDREGION}
  316.     end
  317.     else
  318.     begin
  319.       raise Exception.Create('Level-Format nicht unterstützt oder Datei ist beschädigt');
  320.     end;
  321.   finally
  322.     FreeAndNil(sl);
  323.   end;
  324.   SortEnemies; // Sortierung nach X-Koordinate ist sehr wichtig für das Spiel!
  325. end;
  326.  
  327. procedure TLevelData.Save(filename: string);
  328. var
  329.   sl: TStringList;
  330.   i: integer;
  331. begin
  332.   sl := TStringList.Create;
  333.   try
  334.     sl.Add('[SpaceMission Level, Format 1.2]');
  335.     sl.Add(
  336.       'Width'.PadRight(6, ' ')+
  337.       ' '+
  338.       IntToStr(LevelEditorLength)+
  339.       ' '
  340.     );
  341.     SortEnemies;
  342.     for i := 0 to Length(EnemyAdventTable)-1 do
  343.     begin
  344.       sl.Add(
  345.         'Enemy'.PadRight(6, ' ')+
  346.         ' '+
  347.         IntToStr(Ord(EnemyAdventTable[i].enemyType)).PadRight(6, ' ')+
  348.         ' '+
  349.         IntToStr(EnemyAdventTable[i].x).PadRight(6, ' ')+
  350.         ' '+
  351.         IntToStr(EnemyAdventTable[i].y).PadRight(6, ' ')+
  352.         ' '+
  353.         IntToStr(EnemyAdventTable[i].lifes).PadRight(6, ' ')+
  354.         ' '
  355.       );
  356.     end;
  357.     sl.SaveToFile(filename);
  358.   finally
  359.     FreeAndNil(sl);
  360.   end;
  361. end;
  362.  
  363. procedure TLevelData.SortEnemies;
  364. var
  365.   i, n: integer;
  366.   e: TEnemyAdvent;
  367. begin
  368.   // Bubble Sort Algorithmus
  369.   for n := Length(EnemyAdventTable) downto 2 do
  370.   begin
  371.     for i := 0 to n - 2 do
  372.     begin
  373.       if
  374.         // Sort by X-coord (important for the game!)
  375.         (EnemyAdventTable[i].x > EnemyAdventTable[i+1].x)
  376.         or
  377.         // Sort by Y-coord (just cosmetics)
  378.         ((EnemyAdventTable[i].x = EnemyAdventTable[i+1].x) and (EnemyAdventTable[i].y > EnemyAdventTable[i+1].y))
  379.       then
  380.       begin
  381.         e := EnemyAdventTable[i];
  382.         EnemyAdventTable[i] := EnemyAdventTable[i + 1];
  383.         EnemyAdventTable[i + 1] := e;
  384.       end;
  385.     end;
  386.   end;
  387. end;
  388.  
  389. end.
  390.