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