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