Subversion Repositories jumper

Rev

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.