Subversion Repositories jumper

Rev

Rev 8 | Rev 11 | 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: ;
9 daniel-mar 123
    leInvalidElement: MessageDlg(LNG_LVL_INVALID_ELEMENT, mtError, [mbOk], 0);
124
    leNoIndicator: MessageDlg(LNG_LVL_INVALID_WIDTH, mtError, [mbOk], 0);
125
    leMultipleIndicator: MessageDlg(LNG_LVL_INVALID_MULTIPLE_WIND, mtError, [mbOk], 0);
126
    leLevelIncomplete: MessageDlg(LNG_LVL_INVALID_HEIGHT_MUTLIPLE, mtError, [mbOk], 0);
127
    leHeaderError: MessageDlg(LNG_LVL_INVALID_HEADER, mtError, [mbOk], 0);
128
    leInvalidGoal: MessageDlg(LNG_INVALID_GOAL, mtError, [mbOk], 0);
1 daniel-mar 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.