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. |