Rev 8 | Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | daniel-mar | 1 | unit LevelFunctions; |
2 | |||
3 | interface |
||
4 | |||
5 | uses |
||
6 | SysUtils, Dialogs, Functions, ExtCtrls, Graphics; |
||
7 | |||
8 | type |
||
9 | TFieldType = (ftLocked, ftLockedWithTab, ftEmpty, ftGreen, ftYellow, ftRed); |
||
10 | |||
11 | TFieldProperties = record |
||
12 | Typ: TFieldType; |
||
13 | Goal: Boolean; |
||
14 | end; |
||
15 | |||
16 | TLevelType = (ltStandard, ltDiagonal, ltError); |
||
17 | |||
18 | TLevelArray = array of array of TFieldProperties; |
||
19 | |||
20 | TLevelError = (leNone, leInvalidElement, leNoIndicator, leMultipleIndicator, |
||
21 | leLevelIncomplete, leHeaderError, leInvalidGoal); |
||
22 | |||
23 | procedure DrawLevelPreview(LevelString: string; Image: TImage; BackgroundColor: TColor); |
||
24 | function GetLevelType(LevelString: string): TLevelType; |
||
25 | function CheckLevelIntegrity(LevelString: string; ShowErrors: boolean): TLevelError; overload; |
||
26 | function CheckLevelIntegrity(LevelString: string): TLevelError; overload; |
||
27 | function LevelStringToLevelArray(LevelString: string; ShowErrors: boolean): TLevelArray; |
||
28 | |||
29 | var |
||
30 | AllowDiagonalMoves: boolean; |
||
31 | |||
32 | implementation |
||
33 | |||
34 | const |
||
35 | PREVIEW_BLOCK_SIZE = 10; // Enthält Field und Abstand |
||
36 | PREVIEW_TAB_SIZE = PREVIEW_BLOCK_SIZE div 2; // 5 |
||
37 | HEADER_SIZE = 3; |
||
38 | ERED = '3'; |
||
39 | EYEL = '2'; |
||
40 | EGRE = '1'; |
||
41 | ELOC = 'X'; |
||
42 | EEMP = 'E'; |
||
43 | EIND = '!'; |
||
44 | ETAR = '>'; |
||
45 | ESPE = '*'; |
||
46 | TY_DIA = 'D'; |
||
47 | TY_NOR = 'N'; |
||
48 | |||
49 | resourcestring |
||
50 | LNG_LVL_INVALID_ELEMENT = 'Level invalid: There are invalid elements in the file.'+#13#10#13#10+'Valid elements are "1", "2", "3", "X", "*", "E" and ">" as goal prefix.'; |
||
51 | LNG_LVL_INVALID_WIDTH = 'Level invalid: No width indicator ("!") found.'; |
||
52 | LNG_LVL_INVALID_HEIGHT_MUTLIPLE = 'Level invalid: The level''s actual length is not a multiple of the width.'; |
||
53 | LNG_LVL_INVALID_MULTIPLE_WIND = 'Level invalid: There are multiple width indicators ("!").'; |
||
54 | LNG_LVL_INVALID_HEADER = 'Level invalid: The header is invalid. It does not match the structure "1(D|N)~".'; |
||
55 | LNG_INVALID_GOAL = 'Level invalid: A goal does not point to a valid accessable element ("3", "2", "1" or "E").'; |
||
56 | |||
57 | procedure DrawLevelPreview(LevelString: string; Image: TImage; BackgroundColor: TColor); |
||
58 | var |
||
59 | LevelArray: TLevelArray; |
||
60 | i, j: integer; |
||
61 | t: TFieldType; |
||
62 | halftabs: integer; |
||
63 | begin |
||
64 | LevelArray := nil; |
||
65 | |||
66 | ClearImage(Image, BackgroundColor); |
||
67 | |||
68 | LevelArray := LevelStringToLevelArray(LevelString, false); |
||
69 | |||
70 | for i := Low(LevelArray) to High(LevelArray) do |
||
71 | begin |
||
72 | halftabs := 0; |
||
73 | for j := Low(LevelArray[i]) to High(LevelArray[i]) do |
||
74 | begin |
||
75 | t := LevelArray[i][j].Typ; |
||
76 | |||
77 | case t of |
||
78 | ftLocked: Image.Canvas.Brush.Color := BackgroundColor; |
||
79 | ftLockedWithTab: begin |
||
80 | Image.Canvas.Brush.Color := BackgroundColor; |
||
81 | inc(halftabs); |
||
82 | end; |
||
83 | ftEmpty: Image.Canvas.Brush.Color := clWhite; |
||
84 | ftGreen: Image.Canvas.Brush.Color := clLime; |
||
85 | ftYellow: Image.Canvas.Brush.Color := clYellow; |
||
86 | ftRed: Image.Canvas.Brush.Color := clRed; |
||
87 | end; |
||
88 | |||
89 | if LevelArray[i][j].Goal then |
||
90 | Image.Canvas.Pen.Color := clBlack |
||
91 | else |
||
92 | Image.Canvas.Pen.Color := BackgroundColor; |
||
93 | |||
94 | Image.Canvas.Rectangle(j*PREVIEW_BLOCK_SIZE - halftabs*PREVIEW_TAB_SIZE, i*PREVIEW_BLOCK_SIZE, j*PREVIEW_BLOCK_SIZE - halftabs*PREVIEW_TAB_SIZE+PREVIEW_BLOCK_SIZE, i*PREVIEW_BLOCK_SIZE+PREVIEW_BLOCK_SIZE); |
||
95 | end; |
||
96 | end; |
||
97 | end; |
||
98 | |||
99 | function GetLevelType(LevelString: string): TLevelType; |
||
100 | begin |
||
101 | if CheckLevelIntegrity(LevelString) = leNone then |
||
102 | begin |
||
103 | if Copy(LevelString, 2, 1) = TY_DIA then |
||
104 | begin |
||
105 | result := ltDiagonal; |
||
106 | end |
||
107 | else // if Copy(LevelString, 2, 1) = TY_NOR |
||
108 | begin |
||
109 | result := ltStandard; |
||
110 | end; |
||
111 | end |
||
112 | else |
||
113 | begin |
||
114 | result := ltError; |
||
115 | end; |
||
116 | end; |
||
117 | |||
118 | procedure ShowErrorMessage(error: TLevelError); |
||
119 | begin |
||
120 | case error of |
||
121 | leNone: ; |
||
122 | leInvalidElement: ShowMessage(LNG_LVL_INVALID_ELEMENT); |
||
123 | leNoIndicator: ShowMessage(LNG_LVL_INVALID_WIDTH); |
||
124 | leMultipleIndicator: ShowMessage(LNG_LVL_INVALID_MULTIPLE_WIND); |
||
125 | leLevelIncomplete: ShowMessage(LNG_LVL_INVALID_HEIGHT_MUTLIPLE); |
||
126 | leHeaderError: ShowMessage(LNG_LVL_INVALID_HEADER); |
||
127 | leInvalidGoal: ShowMessage(LNG_INVALID_GOAL); |
||
128 | end; |
||
129 | end; |
||
130 | |||
131 | function CheckLevelIntegrity(LevelString: string; ShowErrors: boolean): TLevelError; |
||
132 | begin |
||
133 | result := CheckLevelIntegrity(LevelString); |
||
134 | if ShowErrors then ShowErrorMessage(result); |
||
135 | end; |
||
136 | |||
137 | function CheckLevelIntegrity(LevelString: string): TLevelError; |
||
138 | var |
||
139 | W: integer; |
||
140 | H: extended; |
||
141 | header, h_ver, h_dia, h_del, tmp: string; |
||
142 | p: integer; |
||
143 | begin |
||
144 | result := leNone; |
||
145 | |||
146 | // Entfernt die Zeilenumbrüche |
||
147 | |||
148 | LevelString := RemoveLineBreaks(LevelString); |
||
149 | |||
150 | // Check 1: Ist der Header OK? |
||
151 | |||
152 | header := copy(LevelString, 1, HEADER_SIZE); |
||
153 | |||
154 | h_ver := copy(header, 1, 1); |
||
155 | if h_ver <> '1' then |
||
156 | begin |
||
157 | result := leHeaderError; |
||
158 | Exit; |
||
159 | end; |
||
160 | |||
161 | h_dia := copy(header, 2, 1); |
||
162 | if (h_dia <> TY_DIA) and (h_dia <> TY_NOR) then |
||
163 | begin |
||
164 | result := leHeaderError; |
||
165 | Exit; |
||
166 | end; |
||
167 | |||
168 | h_del := copy(header, 3, 1); |
||
169 | if h_del <> '~' then |
||
170 | begin |
||
171 | result := leHeaderError; |
||
172 | Exit; |
||
173 | end; |
||
174 | |||
175 | LevelString := copy(LevelString, HEADER_SIZE+1, Length(LevelString)-HEADER_SIZE); |
||
176 | |||
177 | // Check 2: Steht das ggf. vorhandenen ">" vor einem gültigen Feld 1, 2, 3, E? |
||
178 | |||
179 | p := Position(LevelString, ETAR); |
||
180 | |||
181 | while (p <> -1) do |
||
182 | begin |
||
183 | tmp := copy(LevelString, p+1, 1); |
||
184 | |||
185 | if (tmp <> EEMP) and (tmp <> EGRE) and (tmp <> EYEL) and (tmp <> ERED) then |
||
186 | begin |
||
187 | result := leInvalidGoal; |
||
188 | Exit; |
||
189 | end; |
||
190 | |||
191 | LevelString := StringReplace(LevelString, ETAR, '', []); // Dieses Ziel entfernen |
||
192 | |||
193 | p := Position(LevelString, ETAR); |
||
194 | end; |
||
195 | |||
196 | // Check 3: Kommt überhaupt ein "!" vor? |
||
197 | |||
198 | W := Position(LevelString, EIND); |
||
199 | |||
200 | if W = -1 then |
||
201 | begin |
||
202 | result := leNoIndicator; |
||
203 | Exit; |
||
204 | end; |
||
205 | |||
206 | // Check 4: Kam das "!" mehrmals vor? |
||
207 | |||
208 | LevelString := StringReplace(LevelString, EIND, '', []); // Das Erste entfernen |
||
209 | |||
210 | if Position(LevelString, EIND) <> -1 then // gibt es ein Zweites? |
||
211 | begin |
||
212 | result := leMultipleIndicator; |
||
213 | Exit; |
||
214 | end; |
||
215 | |||
216 | // Check 5: Geht das Level nicht in einem Quadrat oder Rechteck auf? |
||
217 | |||
218 | H := (Length(LevelString) - 1) / W; |
||
219 | |||
220 | if not Ganzzahlig(H) then |
||
221 | begin |
||
222 | result := leLevelIncomplete; |
||
223 | Exit; |
||
224 | end; |
||
225 | |||
226 | // Check 6: Gibt es ungültige Elemente im LevelString? |
||
227 | |||
228 | LevelString := StringReplace(LevelString, ESPE, '', [rfReplaceAll]); |
||
229 | LevelString := StringReplace(LevelString, ELOC, '', [rfReplaceAll]); |
||
230 | LevelString := StringReplace(LevelString, EEMP, '', [rfReplaceAll]); |
||
231 | LevelString := StringReplace(LevelString, EGRE, '', [rfReplaceAll]); |
||
232 | LevelString := StringReplace(LevelString, EYEL, '', [rfReplaceAll]); |
||
233 | LevelString := StringReplace(LevelString, ERED, '', [rfReplaceAll]); |
||
234 | |||
235 | if Length(LevelString) > 0 then |
||
236 | begin |
||
237 | result := leInvalidElement; |
||
238 | Exit; |
||
239 | end; |
||
240 | |||
241 | // Check 7: Kann im Level gesprungen werden |
||
242 | |||
243 | { Wird hier nicht abgeprüft, da dafür zuerst der PlayGround gebaut sein muss. |
||
244 | Es ist außerdem eher ein logischer Fehler, kein Fehler in der Levelstruktur! } |
||
245 | end; |
||
246 | |||
247 | function LevelStringToLevelArray(LevelString: string; ShowErrors: boolean): TLevelArray; |
||
248 | var |
||
249 | i, j, j_dec, c: integer; |
||
250 | m: string; |
||
251 | t: TFieldType; |
||
252 | W, H: integer; |
||
253 | err: TLevelError; |
||
254 | NextIsGoal: boolean; |
||
255 | begin |
||
256 | // Zuerst nach Fehlern suchen |
||
257 | err := CheckLevelIntegrity(LevelString, ShowErrors); |
||
258 | if err <> leNone then exit; |
||
259 | |||
260 | // Headerinformationen auslesen |
||
261 | AllowDiagonalMoves := copy(LevelString, 2, 1) = TY_DIA; |
||
262 | |||
263 | // Header entfernen |
||
264 | LevelString := copy(LevelString, HEADER_SIZE+1, Length(LevelString)-HEADER_SIZE); |
||
265 | |||
266 | // Entfernt die Zeilenumbrüche |
||
267 | LevelString := RemoveLineBreaks(LevelString); |
||
268 | |||
269 | // Dimensionen abmessen |
||
270 | W := Position(StringReplace(LevelString, ETAR, '', [rfReplaceAll]), EIND) - 1; |
||
271 | LevelString := StringReplace(LevelString, EIND, '', [rfReplaceAll]); |
||
272 | H := Length(LevelString) div W; |
||
273 | |||
274 | c := 1; |
||
275 | NextIsGoal := false; |
||
276 | |||
277 | SetLength(result, round(H)); |
||
278 | for i := Low(result) to High(result) do |
||
279 | begin |
||
280 | j_dec := 0; |
||
281 | SetLength(result[i], round(W)); |
||
282 | for j := Low(result[i]) to High(result[i])+1 do // +1 wegen dem möglichen zusätzlichem ">" |
||
283 | begin |
||
284 | if (j = High(result[i])+1) and (j_dec = 0) then break; |
||
285 | m := Copy(LevelString, c, 1); |
||
286 | if m = ETAR then |
||
287 | begin |
||
288 | NextIsGoal := true; |
||
289 | inc(j_dec); |
||
290 | end |
||
291 | else |
||
292 | begin |
||
293 | if m = EEMP then t := ftEmpty |
||
294 | else if m = EGRE then t := ftGreen |
||
295 | else if m = EYEL then t := ftYellow |
||
296 | else if m = ERED then t := ftRed |
||
297 | else if m = ESPE then t := ftLockedWithTab |
||
298 | else t := ftLocked; |
||
299 | result[i][j-j_dec].Typ := t; |
||
300 | result[i][j-j_dec].Goal := NextIsGoal; |
||
301 | if NextIsGoal then NextIsGoal := false; |
||
302 | end; |
||
303 | inc(c); |
||
304 | end; |
||
305 | end; |
||
306 | end; |
||
307 | |||
308 | end. |