Subversion Repositories spacemission

Rev

Rev 31 | Rev 40 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
14 daniel-mar 1
unit ComLevelReader;
2
 
3
interface
4
 
27 daniel-mar 5
const
6
  NumEnemyTypes = 7;
7
 
14 daniel-mar 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)
27 daniel-mar 28
  strict private
29
    procedure SortEnemies;
14 daniel-mar 30
  public
31
    LevelEditorLength: integer;
32
    EnemyAdventTable: array of TEnemyAdvent;
27 daniel-mar 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;
14 daniel-mar 39
    procedure Clear;
40
    procedure Load(filename: string);
41
    procedure Save(filename: string);
42
  end;
43
 
17 daniel-mar 44
function GetLevelFileName(lev: integer): string;
45
 
14 daniel-mar 46
implementation
47
 
48
uses
17 daniel-mar 49
  SysUtils, StrUtils, Classes, Global;
14 daniel-mar 50
 
27 daniel-mar 51
const
52
  DefaultLevelLength = 1200;
53
 
17 daniel-mar 54
function GetLevelFileName(lev: integer): string;
55
begin
31 daniel-mar 56
  result := OwnDirectory+'Levels\Level '+inttostr(lev)+'.lev'; // Version 0.3+ Level Files
17 daniel-mar 57
  if not FileExists(Result) then
31 daniel-mar 58
    result := OwnDirectory+'Levels\Lev'+inttostr(lev)+'A1.lev'; // Version 0.2 Level Files
17 daniel-mar 59
end;
60
 
14 daniel-mar 61
{ TLevelData }
62
 
63
procedure TLevelData.Clear;
64
begin
65
  SetLength(EnemyAdventTable, 0);
27 daniel-mar 66
  LevelEditorLength := DefaultLevelLength;
14 daniel-mar 67
end;
68
 
27 daniel-mar 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);
28 daniel-mar 109
 
110
  if enemyType = etEnemyMeteor then lifes := 0;
31 daniel-mar 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]);
28 daniel-mar 113
 
27 daniel-mar 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
 
14 daniel-mar 139
procedure TLevelData.Load(filename: string);
140
var
17 daniel-mar 141
  sl, sl2: TStringList;
14 daniel-mar 142
  curline: integer;
17 daniel-mar 143
  ergebnis: string;
144
  z, act: integer;
145
  i, j: integer;
146
  temp: string;
147
  m: array[1..6] of tstrings;
27 daniel-mar 148
  tmpX, tmpY, tmpLifes: integer;
149
  tmpEnemy: TEnemyType;
30 daniel-mar 150
  tmpRest: string;
14 daniel-mar 151
begin
27 daniel-mar 152
  Clear;
153
 
15 daniel-mar 154
  sl := TStringList.Create;
14 daniel-mar 155
  try
17 daniel-mar 156
    if EndsText('A1.lev', filename) then
157
    begin
30 daniel-mar 158
      {$REGION 'Backwards compatibility level format 0.2 (split into 5-6 files; convert to 0.3)'}
17 daniel-mar 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';
14 daniel-mar 174
 
17 daniel-mar 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
14 daniel-mar 212
    begin
17 daniel-mar 213
      sl.LoadFromFile(filename);
214
    end;
215
 
216
    if sl.Strings[0] = '; SpaceMission 0.3' then
217
    begin
30 daniel-mar 218
      {$REGION 'Backwards compatibility level format 0.3 (convert to 0.4)'}
17 daniel-mar 219
      sl.Strings[0] := '; SpaceMission 0.4';
30 daniel-mar 220
      sl.Insert(1, '; LEV-File');
17 daniel-mar 221
      {$ENDREGION}
222
    end;
223
 
30 daniel-mar 224
    if (sl.Strings[0] = '; SpaceMission 0.4') and
225
       (sl.Strings[1] = '; LEV-File') then
17 daniel-mar 226
    begin
30 daniel-mar 227
      {$REGION 'Backwards compatibility level format 0.4 (convert to 1.0)'}
17 daniel-mar 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
27 daniel-mar 243
              sl2.Add(IntTostr(DefaultLevelLength))
17 daniel-mar 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
 
30 daniel-mar 264
    if (sl.Strings[0] = '; SpaceMission 1.0') and
265
       (sl.Strings[1] = '; LEV-File') then
17 daniel-mar 266
    begin
267
      {$REGION 'Level format 1.0'}
268
      LevelEditorLength := StrToInt(sl.Strings[2]);
269
      curline := 3;
15 daniel-mar 270
      while curline < sl.Count do
14 daniel-mar 271
      begin
27 daniel-mar 272
        tmpEnemy := TEnemyType(strtoint(sl.Strings[curline]));
20 daniel-mar 273
        Inc(curline);
27 daniel-mar 274
        tmpX := strtoint(sl.Strings[curline]);
20 daniel-mar 275
        Inc(curline);
27 daniel-mar 276
        tmpY := strtoint(sl.Strings[curline]);
20 daniel-mar 277
        Inc(curline);
27 daniel-mar 278
        tmpLifes := strtoint(sl.Strings[curline]);
20 daniel-mar 279
        Inc(curline);
27 daniel-mar 280
        AddEnemy(tmpX, tmpY, tmpEnemy, tmpLifes);
14 daniel-mar 281
      end;
17 daniel-mar 282
      {$ENDREGION}
14 daniel-mar 283
    end
34 daniel-mar 284
    else if SameText(sl.Strings[0], '[SpaceMission Level, Format 1.2]') then
30 daniel-mar 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
14 daniel-mar 317
    else
318
    begin
30 daniel-mar 319
      raise Exception.Create('Level-Format nicht unterstützt oder Datei ist beschädigt');
14 daniel-mar 320
    end;
321
  finally
15 daniel-mar 322
    FreeAndNil(sl);
14 daniel-mar 323
  end;
30 daniel-mar 324
  SortEnemies; // Sortierung nach X-Koordinate ist sehr wichtig für das Spiel!
14 daniel-mar 325
end;
326
 
327
procedure TLevelData.Save(filename: string);
15 daniel-mar 328
var
329
  sl: TStringList;
330
  i: integer;
14 daniel-mar 331
begin
15 daniel-mar 332
  sl := TStringList.Create;
333
  try
30 daniel-mar 334
    sl.Add('[SpaceMission Level, Format 1.2]');
335
    sl.Add(
336
      'Width'.PadRight(6, ' ')+
337
      ' '+
338
      IntToStr(LevelEditorLength)+
339
      ' '
340
    );
27 daniel-mar 341
    SortEnemies;
15 daniel-mar 342
    for i := 0 to Length(EnemyAdventTable)-1 do
343
    begin
30 daniel-mar 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
      );
15 daniel-mar 356
    end;
357
    sl.SaveToFile(filename);
358
  finally
359
    FreeAndNil(sl);
360
  end;
14 daniel-mar 361
end;
362
 
27 daniel-mar 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
 
14 daniel-mar 389
end.