Subversion Repositories spacemission

Rev

Rev 66 | Rev 79 | 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
 
40 daniel-mar 5
uses
6
  Classes;
7
 
14 daniel-mar 8
type
72 daniel-mar 9
  // If you add a new enemy or item, please edit
10
  // - ComLevelReader.pas : EnemyTypeHasLives()
11
  // - GamMain.pas : TMainForm.SceneMain
12
  // - LevMain.pas : * GUI
13
  //                 * TMainForm.SelectedEnemyType
14
  //                 * TEnemy.Create
15
  //                 * TMainForm.DXDrawMouseMove
16
  //                 * TMainForm.DXDrawMouseDown
14 daniel-mar 17
  TEnemyType = (
18
    etUnknown,
19
    etEnemyAttacker,
20
    etEnemyAttacker2,
21
    etEnemyAttacker3,
22
    etEnemyMeteor,
23
    etEnemyUFO,
24
    etEnemyUFO2,
72 daniel-mar 25
    etEnemyBoss,
26
    etItemMedikit
14 daniel-mar 27
  );
28
 
29
  TEnemyAdvent = record
30
    enemyType: TEnemyType;
31
    x: integer;
32
    y: integer;
33
    lifes: integer;
34
  end;
35
 
40 daniel-mar 36
  TLevelData = class(TPersistent)
27 daniel-mar 37
  strict private
38
    procedure SortEnemies;
40 daniel-mar 39
  strict protected
40
    procedure AssignTo(Dest: TPersistent); override;
14 daniel-mar 41
  public
40 daniel-mar 42
    RasterErzwingen: boolean;
14 daniel-mar 43
    LevelEditorLength: integer;
42 daniel-mar 44
    LevelName: string;
45
    LevelAuthor: string;
14 daniel-mar 46
    EnemyAdventTable: array of TEnemyAdvent;
27 daniel-mar 47
    function IndexOfEnemy(x,y:integer;enemyType:TEnemyType;lifes:integer): integer;
48
    procedure AddEnemy(x,y:integer;enemyType:TEnemyType;lifes:integer);
49
    procedure DeleteEnemy(i: integer); overload;
50
    procedure DeleteEnemy(x,y:integer;enemyType:TEnemyType;lifes:integer); overload;
51
    function CountEnemies: integer;
52
    function HasBoss: boolean;
14 daniel-mar 53
    procedure Clear;
40 daniel-mar 54
    procedure LoadFromStrings(sl: TStrings); // version 0.3 - version 1.2 files
55
    procedure LoadFromFile(filename: string); // version 0.2 - version 1.2 files
56
    procedure SaveToStrings(sl: TStrings);
57
    procedure SaveToFile(filename: string);
58
    destructor Destroy; override;
14 daniel-mar 59
  end;
60
 
65 daniel-mar 61
  TGameMode = (gmUnknown, gmLevels, gmRandom, gmEditor);
40 daniel-mar 62
 
63
  TSaveData = class(TPersistent)
64
  strict protected
65
    procedure AssignTo(Dest: TPersistent); override;
66
  public
67
    Score: integer;
68
    Life: integer;
69
    Level: integer;
70
    GameMode: TGameMode;
71
    LevelData: TLevelData;
72
    procedure Clear;
73
    procedure LoadFromStrings(sl: TStrings);
74
    procedure LoadFromFile(filename: string);
75
    procedure SaveToStrings(sl: TStrings);
76
    procedure SaveToFile(filename: string);
77
    destructor Destroy; override;
78
  end;
79
 
61 daniel-mar 80
  TLevelFile = record
81
    levelNumber: integer;
82
    fileLocation: string;
83
    isUser: boolean;
84
    found: boolean;
85
  end;
17 daniel-mar 86
 
61 daniel-mar 87
function GetLevelFileName(lev: integer; forceuserdir: boolean): TLevelFile;
88
 
72 daniel-mar 89
function EnemyTypeHasLives(et: TEnemyType): boolean;
90
 
14 daniel-mar 91
implementation
92
 
93
uses
52 daniel-mar 94
  SysUtils, StrUtils, Global, Windows, System.Types;
14 daniel-mar 95
 
52 daniel-mar 96
const
97
  // { iso(1) identified-organization(3) dod(6) internet(1) private(4) enterprise(1) 37476 products(2) spacemission(8) file-format(1) lev-sav-v12(1) }
98
  // https://hosted.oidplus.com/viathinksoft/?goto=oid%3A1.3.6.1.4.1.37476.2.8.1.1
99
  OID_LEVSAV_VER12 = '1.3.6.1.4.1.37476.2.8.1.1';
100
 
61 daniel-mar 101
function GetLevelFileName(lev: integer; forceuserdir: boolean): TLevelFile;
27 daniel-mar 102
 
51 daniel-mar 103
  function _GetLevelVerzeichnisSystem: string;
104
  begin
105
    // Für die Auslieferungs-Levels
106
    result := OwnDirectory + 'Levels';
107
  end;
108
 
109
  function _GetLevelVerzeichnisUser: string;
110
  begin
111
    try
112
      result := GetKnownFolderPath(FOLDERID_SavedGames);
113
    except
114
      result := '';
115
    end;
116
    if result = '' then
117
    begin
118
      // Pre Vista
119
      result := OwnDirectory + 'Levels';
120
    end
121
    else
122
    begin
123
      result := IncludeTrailingPathDelimiter(result);
124
      result := result + 'SpaceMission';
125
    end;
126
    result := IncludeTrailingPathDelimiter(result);
127
    ForceDirectories(result);
128
  end;
129
 
130
  function _GetLevelFileNameUser(lev: integer): string;
131
  var
132
    old, new: string;
133
  begin
134
    new := IncludeTrailingPathDelimiter(_GetLevelVerzeichnisUser)+'Level '+inttostr(lev)+'.lev'; // Version 0.3+ Level Files
135
    old := IncludeTrailingPathDelimiter(_GetLevelVerzeichnisUser)+'Lev'+inttostr(lev)+'A1.lev'; // Version 0.2 Level Files
136
    if fileexists(new) then exit(new);
137
    if fileexists(old) then exit(old);
138
    exit(new);
139
  end;
140
 
141
  function _GetLevelFileNameSystem(lev: integer): string;
142
  var
143
    old, new: string;
144
  begin
145
    new := IncludeTrailingPathDelimiter(_GetLevelVerzeichnisSystem)+'Level '+inttostr(lev)+'.lev'; // Version 0.3+ Level Files
146
    old := IncludeTrailingPathDelimiter(_GetLevelVerzeichnisSystem)+'Lev'+inttostr(lev)+'A1.lev'; // Version 0.2 Level Files
147
    if fileexists(new) then exit(new);
148
    if fileexists(old) then exit(old);
149
    exit(new);
150
  end;
151
 
152
var
153
  usr, sys: string;
61 daniel-mar 154
  bfound: boolean;
17 daniel-mar 155
begin
61 daniel-mar 156
  result.levelNumber := lev;
51 daniel-mar 157
  usr := _GetLevelFileNameUser(lev);
158
  sys := _GetLevelFileNameSystem(lev);
61 daniel-mar 159
  bfound := fileexists(usr);
160
  if bfound or forceuserdir then
161
  begin
162
    result.isUser := true;
163
    result.fileLocation := usr;
164
    result.found := bfound;
165
    exit;
166
  end;
167
  bfound := fileexists(sys);
168
  if bfound then
169
  begin
170
    result.isUser := false;
171
    result.fileLocation := sys;
172
    result.found := bfound;
173
    exit;
174
  end;
175
  result.isUser := true;
176
  result.fileLocation := usr;
177
  result.found := false;
17 daniel-mar 178
end;
179
 
52 daniel-mar 180
// this is just an example, there are many
181
// different ways you can implement this
182
// more efficiently, ie using a TStringBuilder,
183
// or even modifying the String in-place...
184
function CollapseSpaces(const S: string): string;
185
var
186
  P: PChar;
187
  AddSpace: Boolean;
188
begin
189
  Result := '';
190
  AddSpace := False;
191
  P := PChar(S);
192
  while P^ <> #0 do
193
  begin
194
    while CharInSet(P^, [#1..' ']) do Inc(P);
195
    if P^ = #0 then Exit;
196
    if AddSpace then
197
      Result := Result + ' '
198
    else
199
      AddSpace := True;
200
    repeat
201
      Result := Result + P^;
202
      Inc(P);
203
    until P^ <= ' ';
204
  end;
205
end;
206
 
14 daniel-mar 207
{ TLevelData }
208
 
40 daniel-mar 209
procedure TLevelData.AssignTo(Dest: TPersistent);
210
var
211
  DestLevelData: TLevelData;
212
  i: integer;
213
begin
214
  DestLevelData := Dest as TLevelData;
215
  if Assigned(DestLevelData) then
216
  begin
217
    DestLevelData.RasterErzwingen := Self.RasterErzwingen;
218
    DestLevelData.LevelEditorLength := Self.LevelEditorLength;
42 daniel-mar 219
    DestLevelData.LevelName := Self.LevelName;
220
    DestLevelData.LevelAuthor := Self.LevelAuthor;
40 daniel-mar 221
    SetLength(DestLevelData.EnemyAdventTable, Length(Self.EnemyAdventTable));
66 daniel-mar 222
    for i := 0 to Length(Self.EnemyAdventTable)-1 do
40 daniel-mar 223
    begin
224
      DestLevelData.EnemyAdventTable[i] := Self.EnemyAdventTable[i];
225
    end;
226
  end
227
  else
228
  begin
229
    inherited;
230
  end;
231
end;
232
 
14 daniel-mar 233
procedure TLevelData.Clear;
234
begin
235
  SetLength(EnemyAdventTable, 0);
27 daniel-mar 236
  LevelEditorLength := DefaultLevelLength;
42 daniel-mar 237
  LevelName := '';
238
  LevelAuthor := '';
14 daniel-mar 239
end;
240
 
27 daniel-mar 241
function TLevelData.CountEnemies: integer;
242
begin
243
  result := Length(EnemyAdventTable);
244
end;
245
 
246
procedure TLevelData.DeleteEnemy(i: integer);
247
var
248
  j: integer;
249
begin
250
  for j := i+1 to CountEnemies-1 do
251
  begin
252
    EnemyAdventTable[j-1] := EnemyAdventTable[j];
253
  end;
254
  SetLength(EnemyAdventTable, Length(EnemyAdventTable)-1);
255
end;
256
 
257
procedure TLevelData.DeleteEnemy(x, y: integer; enemyType: TEnemyType;
258
  lifes: integer);
259
begin
260
  DeleteEnemy(IndexOfEnemy(x, y, enemyType, lifes));
261
end;
262
 
40 daniel-mar 263
destructor TLevelData.Destroy;
264
begin
265
  Clear;
266
  inherited;
267
end;
268
 
27 daniel-mar 269
function TLevelData.HasBoss: boolean;
270
var
271
  i: integer;
272
begin
273
  for i := 0 to Length(EnemyAdventTable) - 1 do
274
  begin
275
    if EnemyAdventTable[i].enemyType = etEnemyBoss then
276
    begin
277
      result := true;
278
      exit;
279
    end;
280
  end;
281
  result := false;
282
end;
283
 
284
procedure TLevelData.AddEnemy(x,y:integer;enemyType:TEnemyType;lifes:integer);
285
begin
286
  SetLength(EnemyAdventTable, Length(EnemyAdventTable)+1);
28 daniel-mar 287
 
288
  if enemyType = etEnemyMeteor then lifes := 0;
40 daniel-mar 289
  if RasterErzwingen then
290
  begin
291
    if x mod RasterW <> 0 then raise Exception.CreateFmt('X-Koordinate muss ohne Rest durch %d teilbar sein', [RasterW]);
292
    if y mod RasterH <> 0 then raise Exception.CreateFmt('Y-Koordinate muss ohne Rest durch %d teilbar sein', [RasterH]);
293
  end;
294
  if lifes > MaxPossibleEnemyLives then lifes := MaxPossibleEnemyLives;
28 daniel-mar 295
 
27 daniel-mar 296
  EnemyAdventTable[Length(EnemyAdventTable)-1].x         := x;
297
  EnemyAdventTable[Length(EnemyAdventTable)-1].y         := y;
298
  EnemyAdventTable[Length(EnemyAdventTable)-1].enemyType := enemyType;
299
  EnemyAdventTable[Length(EnemyAdventTable)-1].lifes     := lifes;
300
end;
301
 
302
function TLevelData.IndexOfEnemy(x, y: integer; enemyType: TEnemyType;
303
  lifes: integer): integer;
304
var
305
  i: integer;
306
begin
307
  for i := 0 to Length(EnemyAdventTable) - 1 do
308
  begin
309
    if (EnemyAdventTable[i].x = x) and
310
       (EnemyAdventTable[i].y = y) and
311
       (EnemyAdventTable[i].enemyType = enemyType) and
312
       (EnemyAdventTable[i].lifes = lifes) then
313
    begin
314
      result := i;
315
      exit;
316
    end;
317
  end;
318
  result := -1;
319
end;
320
 
40 daniel-mar 321
procedure TLevelData.LoadFromStrings(sl: TStrings);
14 daniel-mar 322
var
323
  curline: integer;
17 daniel-mar 324
  z, act: integer;
40 daniel-mar 325
  sl2: TStringList;
27 daniel-mar 326
  tmpX, tmpY, tmpLifes: integer;
327
  tmpEnemy: TEnemyType;
40 daniel-mar 328
  ergebnis: string;
52 daniel-mar 329
  ary: TStringDynArray;
330
  sLine: string;
14 daniel-mar 331
begin
27 daniel-mar 332
  Clear;
333
 
42 daniel-mar 334
  LevelEditorLength := DefaultLevelLength;
335
  LevelName := '';
336
  LevelAuthor := '';
337
 
52 daniel-mar 338
  if sl.Strings[0] = '; SpaceMission 0.3' then // do not localize
40 daniel-mar 339
  begin
340
    {$REGION 'Backwards compatibility level format 0.3 (convert to 0.4)'}
52 daniel-mar 341
    sl.Strings[0] := '; SpaceMission 0.4'; // do not localize
342
    sl.Insert(1, '; LEV-File'); // do not localize
40 daniel-mar 343
    {$ENDREGION}
344
  end;
345
 
52 daniel-mar 346
  if (sl.Strings[0] = '; SpaceMission 0.4') and // do not localize
347
     (sl.Strings[1] = '; LEV-File') then // do not localize
40 daniel-mar 348
  begin
349
    {$REGION 'Backwards compatibility level format 0.4 (convert to 1.0)'}
350
    sl2 := TStringList.Create;
351
    try
352
      z := 0;
353
      act := 0;
354
      while z < sl.Count do
355
      begin
356
        inc(z);
357
        if z > 2 then inc(act);
358
        if act = 5 then act := 1;
359
        ergebnis := sl.Strings[z-1];
360
        if ergebnis = '; SpaceMission 0.4' then
361
          sl2.Add('; SpaceMission 1.0')
362
        else
363
        begin
364
          if (ergebnis = '30000') and (z = 3) then
365
            sl2.Add(IntTostr(DefaultLevelLength))
366
          else
367
          begin
368
            //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
369
            if (z < 4) or (z > 7) then
370
            begin
371
              if act = 4 then
372
                sl2.Add(inttostr(strtoint(ergebnis) + 32 - (5 * (strtoint(ergebnis) div 37))))
373
              else
374
                sl2.Add(Ergebnis);
375
            end;
376
          end;
377
        end;
378
      end;
379
      sl.Text := sl2.Text;
380
    finally
381
      FreeAndNil(sl2);
382
    end;
383
    {$ENDREGION}
384
  end;
385
 
52 daniel-mar 386
  if (sl.Strings[0] = '; SpaceMission 1.0') and // do not localize
387
     (sl.Strings[1] = '; LEV-File') then // do not localize
40 daniel-mar 388
  begin
389
    {$REGION 'Level format 1.0'}
390
    LevelEditorLength := StrToInt(sl.Strings[2]);
391
    curline := 3;
392
    while curline < sl.Count do
393
    begin
394
      tmpEnemy := TEnemyType(strtoint(sl.Strings[curline]));
395
      Inc(curline);
396
      tmpX := strtoint(sl.Strings[curline]);
397
      Inc(curline);
398
      tmpY := strtoint(sl.Strings[curline]);
399
      Inc(curline);
400
      tmpLifes := strtoint(sl.Strings[curline]);
401
      Inc(curline);
402
      AddEnemy(tmpX, tmpY, tmpEnemy, tmpLifes);
403
    end;
404
    {$ENDREGION}
405
  end
52 daniel-mar 406
  else if (SameText(sl.Strings[0], '['+OID_LEVSAV_VER12+']')) then
40 daniel-mar 407
  begin
408
    {$REGION 'Level format 1.2'}
409
    for curline := 1 to sl.Count-1 do
410
    begin
52 daniel-mar 411
      sLine := sl.Strings[curline].Trim;
412
      if (sLine = '') or (Copy(sLine, 1, 1) = ';') then continue;
413
      ary := SplitString(CollapseSpaces(sLine), ' ');
53 daniel-mar 414
      if SameText(ary[0], 'Width') then // do not localize
40 daniel-mar 415
      begin
52 daniel-mar 416
        LevelEditorLength := StrToInt(ary[1]);
417
        if (Length(ary) > 2) and (Copy(ary[2], 1, 1) <> ';') then
418
          raise Exception.CreateFmt('Zeile %d ist ungültig (Zusatzinfo am Ende)', [curline+1]);
40 daniel-mar 419
      end
53 daniel-mar 420
      else if SameText(ary[0], 'Name') then // do not localize
42 daniel-mar 421
      begin
53 daniel-mar 422
        LevelName := Trim(Copy(sLine, Length(ary[0])+2, Length(sLine)));
42 daniel-mar 423
      end
53 daniel-mar 424
      else if SameText(ary[0], 'Author') then // do not localize
42 daniel-mar 425
      begin
53 daniel-mar 426
        LevelAuthor := Trim(Copy(sLine, Length(ary[0])+2, Length(sLine)));
42 daniel-mar 427
      end
53 daniel-mar 428
      else if SameText(ary[0], 'Enemy') then // do not localize
40 daniel-mar 429
      begin
52 daniel-mar 430
        tmpEnemy := TEnemyType(strtoint(ary[1]));
431
        tmpX     := strtoint(ary[2]);
432
        tmpY     := strtoint(ary[3]);
433
        tmpLifes := strtoint(ary[4]);
434
        if (Length(ary) > 5) and (Copy(ary[5], 1, 1) <> ';') then
40 daniel-mar 435
          raise Exception.CreateFmt('Zeile %d ist ungültig (Zusatzinfo am Ende)', [curline+1]);
436
        AddEnemy(tmpX, tmpY, tmpEnemy, tmpLifes);
437
      end;
438
    end;
439
    {$ENDREGION}
440
  end
441
  else
442
  begin
443
    raise Exception.Create('Level-Format nicht unterstützt oder Datei ist beschädigt');
444
  end;
445
 
446
  SortEnemies; // Sortierung nach X-Koordinate ist sehr wichtig für das Spiel!
447
end;
448
 
449
procedure TLevelData.LoadFromFile(filename: string);
450
var
451
  sl: TStringList;
452
  i, j: integer;
453
  temp: string;
454
  m: array[1..6] of tstrings;
455
begin
15 daniel-mar 456
  sl := TStringList.Create;
14 daniel-mar 457
  try
52 daniel-mar 458
    if EndsText('A1.lev', filename) then // do not localize
17 daniel-mar 459
    begin
30 daniel-mar 460
      {$REGION 'Backwards compatibility level format 0.2 (split into 5-6 files; convert to 0.3)'}
17 daniel-mar 461
      m[1] := TStringList.create;
462
      m[2] := TStringList.create;
463
      m[3] := TStringList.create;
464
      m[4] := TStringList.create;
465
      m[5] := TStringList.create;
466
      m[6] := TStringList.create;
467
      try
468
        for i := 1 to 6 do
469
        begin
470
          filename[Length(filename)-4] := IntToStr(i)[1]; // ...A2.sav, ...A3.sav, etc.
471
          if FileExists(filename) then
472
            m[i].loadfromfile(filename);
473
        end;
474
        m[1].strings[0] := '-624';
475
        if m[6].Text = '' then m[6].Text := '30000';
14 daniel-mar 476
 
52 daniel-mar 477
        sl.Add('; SpaceMission 0.3'); // do not localize
17 daniel-mar 478
        sl.Add(temp);
479
        for j := 0 to m[1].count-2 do
480
        begin
481
          for i := 0 to m[1].count-2 do
482
          begin
483
            if strtoint(m[1].strings[i]) > strtoint(m[1].strings[i+1]) then
484
            begin
485
              m[1].exchange(i, i+1);
486
              m[2].exchange(i, i+1);
487
              m[3].exchange(i, i+1);
488
              m[4].exchange(i, i+1);
489
              m[5].exchange(i, i+1);
490
            end;
491
          end;
492
        end;
493
        for i := 0 to m[3].count-1 do
494
        begin
495
          for j := 1 to 4 do
496
          begin
497
            if j = 1 then sl.Add(m[3].strings[i]);
498
            if j = 2 then sl.Add(m[1].strings[i]);
499
            if j = 3 then sl.Add(m[2].strings[i]);
500
            if j = 4 then sl.Add(m[4].strings[i]);
501
          end;
502
        end;
503
      finally
504
        FreeAndNil(m[1]);
505
        FreeAndNil(m[2]);
506
        FreeAndNil(m[3]);
507
        FreeAndNil(m[4]);
508
        FreeAndNil(m[5]);
509
        FreeAndNil(m[6]);
510
      end;
511
      {$ENDREGION}
512
    end
513
    else
14 daniel-mar 514
    begin
17 daniel-mar 515
      sl.LoadFromFile(filename);
516
    end;
517
 
40 daniel-mar 518
    LoadFromStrings(sl);
14 daniel-mar 519
  finally
15 daniel-mar 520
    FreeAndNil(sl);
14 daniel-mar 521
  end;
522
end;
523
 
40 daniel-mar 524
procedure TLevelData.SaveToStrings(sl: TStrings);
15 daniel-mar 525
var
526
  i: integer;
14 daniel-mar 527
begin
40 daniel-mar 528
  sl.Clear;
52 daniel-mar 529
  sl.Add('['+OID_LEVSAV_VER12+']');
530
  if LevelName   <> '' then sl.Add('Name   ' + LevelName); // do not localize
531
  if LevelAuthor <> '' then sl.Add('Author ' + LevelAuthor); // do not localize
532
  sl.Add('Width  ' + IntToStr(LevelEditorLength)); // do not localize
40 daniel-mar 533
  SortEnemies;
48 daniel-mar 534
  sl.Add(';      Type   XCoord YCoord Lives');
40 daniel-mar 535
  for i := 0 to Length(EnemyAdventTable)-1 do
536
  begin
52 daniel-mar 537
    sl.Add(Trim(
538
      'Enemy'.PadRight(6, ' ')+ // do not localize
30 daniel-mar 539
      ' '+
40 daniel-mar 540
      IntToStr(Ord(EnemyAdventTable[i].enemyType)).PadRight(6, ' ')+
541
      ' '+
542
      IntToStr(EnemyAdventTable[i].x).PadRight(6, ' ')+
543
      ' '+
544
      IntToStr(EnemyAdventTable[i].y).PadRight(6, ' ')+
545
      ' '+
546
      IntToStr(EnemyAdventTable[i].lifes).PadRight(6, ' ')+
30 daniel-mar 547
      ' '
52 daniel-mar 548
    ));
40 daniel-mar 549
  end;
550
end;
551
 
552
procedure TLevelData.SaveToFile(filename: string);
553
var
554
  sl: TStringList;
555
begin
556
  sl := TStringList.Create;
557
  try
558
    SaveToStrings(sl);
15 daniel-mar 559
    sl.SaveToFile(filename);
560
  finally
561
    FreeAndNil(sl);
562
  end;
14 daniel-mar 563
end;
564
 
27 daniel-mar 565
procedure TLevelData.SortEnemies;
566
var
567
  i, n: integer;
568
  e: TEnemyAdvent;
569
begin
570
  // Bubble Sort Algorithmus
571
  for n := Length(EnemyAdventTable) downto 2 do
572
  begin
573
    for i := 0 to n - 2 do
574
    begin
575
      if
576
        // Sort by X-coord (important for the game!)
577
        (EnemyAdventTable[i].x > EnemyAdventTable[i+1].x)
578
        or
579
        // Sort by Y-coord (just cosmetics)
580
        ((EnemyAdventTable[i].x = EnemyAdventTable[i+1].x) and (EnemyAdventTable[i].y > EnemyAdventTable[i+1].y))
581
      then
582
      begin
583
        e := EnemyAdventTable[i];
584
        EnemyAdventTable[i] := EnemyAdventTable[i + 1];
585
        EnemyAdventTable[i + 1] := e;
586
      end;
587
    end;
588
  end;
589
end;
590
 
40 daniel-mar 591
{ TSaveData }
592
 
593
procedure TSaveData.AssignTo(Dest: TPersistent);
594
var
595
  DestSaveData: TSaveData;
596
begin
597
  DestSaveData := Dest as TSaveData;
598
  if Assigned(DestSaveData) then
599
  begin
600
    DestSaveData.Score := Self.Score;
601
    DestSaveData.Life := Self.Life;
602
    DestSaveData.Level := Self.Level;
603
    DestSaveData.GameMode := Self.GameMode;
604
    if not Assigned(DestSaveData.LevelData) then DestSaveData.LevelData := TLevelData.Create;
605
    DestSaveData.LevelData.Assign(Self.LevelData);
606
  end
607
  else
608
  begin
609
    inherited;
610
  end;
611
end;
612
 
613
procedure TSaveData.Clear;
614
begin
615
  Score := 0;
616
  Life := 0;
617
  Level := 0;
618
  GameMode := gmUnknown;
619
  FreeAndNil(LevelData);
620
end;
621
 
622
destructor TSaveData.Destroy;
623
begin
624
  Clear;
625
  inherited;
626
end;
627
 
628
procedure TSaveData.SaveToStrings(sl: TStrings);
629
var
630
  sl2: TStringList;
631
begin
632
  sl2 := TStringList.Create;
633
  try
52 daniel-mar 634
    sl.Add('['+OID_LEVSAV_VER12+']');
635
    sl.Add('Score  ' + IntToStr(Score)); // do not localize
636
    sl.Add('Lives  ' + IntToStr(Life)); // do not localize
637
    sl.Add('Level  ' + IntToStr(Level)); // do not localize
638
    sl.Add('Mode   ' + IntToStr(Ord(GameMode))); // do not localize
40 daniel-mar 639
    LevelData.SaveToStrings(sl2);
52 daniel-mar 640
    sl2.Delete(0); // Delete additional level signature
40 daniel-mar 641
    sl.AddStrings(sl2);
642
  finally
643
    FreeAndNil(sl2);
644
  end;
645
end;
646
 
647
procedure TSaveData.LoadFromStrings(sl: TStrings);
648
var
649
  curline: Integer;
52 daniel-mar 650
  ary: TStringDynArray;
651
  sLine: string;
40 daniel-mar 652
begin
52 daniel-mar 653
  if (sl.Strings[0] = '; SpaceMission 1.0') and // do not localize
654
     (sl.Strings[1] = '; SAV-File') then // do not localize
40 daniel-mar 655
  begin
656
    Score    := StrToInt(sl.Strings[2]);
657
    Life     := StrToInt(sl.Strings[3]);
658
    Level    := StrToInt(sl.Strings[4]);
659
    GameMode := TGameMode(StrToInt(sl.Strings[5]));
660
    if Assigned(LevelData) then FreeAndNil(LevelData);
661
  end
52 daniel-mar 662
  else if SameText(sl.Strings[0], '['+OID_LEVSAV_VER12+']') then
40 daniel-mar 663
  begin
664
    Score    := 0;
665
    Life     := 0;
666
    Level    := 0;
667
    GameMode := gmUnknown;
668
    for curline := 1 to sl.Count-1 do
669
    begin
52 daniel-mar 670
      sLine := sl.Strings[curline].Trim;
671
      if (sLine = '') or (Copy(sLine, 1, 1) = ';') then continue;
672
      ary := SplitString(CollapseSpaces(sLine), ' ');
53 daniel-mar 673
      if SameText(ary[0], 'Score') then // do not localize
40 daniel-mar 674
      begin
52 daniel-mar 675
        Score := StrToInt(ary[1]);
676
        if (Length(ary) > 2) and (Copy(ary[2], 1, 1) <> ';') then
677
          raise Exception.CreateFmt('Zeile %d ist ungültig (Zusatzinfo am Ende)', [curline+1]);
40 daniel-mar 678
      end
53 daniel-mar 679
      else if SameText(ary[0], 'Lives') then // do not localize
40 daniel-mar 680
      begin
52 daniel-mar 681
        Life := StrToInt(ary[1]);
682
        if (Length(ary) > 2) and (Copy(ary[2], 1, 1) <> ';') then
683
          raise Exception.CreateFmt('Zeile %d ist ungültig (Zusatzinfo am Ende)', [curline+1]);
40 daniel-mar 684
      end
53 daniel-mar 685
      else if SameText(ary[0], 'Level') then // do not localize
40 daniel-mar 686
      begin
52 daniel-mar 687
        Level := StrToInt(ary[1]);
688
        if (Length(ary) > 2) and (Copy(ary[2], 1, 1) <> ';') then
689
          raise Exception.CreateFmt('Zeile %d ist ungültig (Zusatzinfo am Ende)', [curline+1]);
40 daniel-mar 690
      end
53 daniel-mar 691
      else if SameText(ary[0], 'Mode') then // do not localize
40 daniel-mar 692
      begin
52 daniel-mar 693
        GameMode := TGameMode(StrToInt(ary[1]));
694
        if (Length(ary) > 2) and (Copy(ary[2], 1, 1) <> ';') then
695
          raise Exception.CreateFmt('Zeile %d ist ungültig (Zusatzinfo am Ende)', [curline+1]);
40 daniel-mar 696
      end;
697
    end;
698
    if Assigned(LevelData) then FreeAndNil(LevelData);
699
    LevelData := TLevelData.Create;
44 daniel-mar 700
    LevelData.RasterErzwingen := false;
40 daniel-mar 701
    LevelData.LoadFromStrings(sl);
702
  end
703
  else
704
  begin
705
    raise Exception.Create('Spielstand-Format nicht unterstützt oder Datei beschädigt');
706
  end;
707
end;
708
 
709
procedure TSaveData.LoadFromFile(filename: string);
710
var
711
  sl: TStringList;
712
begin
713
  sl := TStringList.Create;
714
  try
715
    sl.LoadFromFile(filename);
716
    LoadFromStrings(sl);
717
  finally
718
    FreeAndNil(sl);
719
  end;
720
end;
721
 
722
procedure TSaveData.SaveToFile(filename: string);
723
var
724
  sl: TStringList;
725
begin
726
  sl := TStringList.Create;
727
  try
728
    SaveToStrings(sl);
729
    sl.SaveToFile(filename);
730
  finally
731
    FreeAndNil(sl);
732
  end;
733
end;
734
 
72 daniel-mar 735
function EnemyTypeHasLives(et: TEnemyType): boolean;
736
begin
737
  result := (et <> etEnemyMeteor) and (et <> etItemMedikit);
738
end;
739
 
14 daniel-mar 740
end.