Subversion Repositories jumper

Rev

Rev 11 | Rev 21 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 11 Rev 19
1
unit LevelFunctions;
1
unit LevelFunctions;
2
 
2
 
3
interface
3
interface
4
 
4
 
5
uses
5
uses
6
  SysUtils, Dialogs, Functions, ExtCtrls, Graphics, Classes, Math;
6
  SysUtils, Dialogs, Functions, ExtCtrls, Graphics, Classes, Math;
7
 
7
 
8
type
8
type
9
  TFieldType = (ftFullSpace, ftHalfSpace, ftEmpty, ftRed, ftYellow, ftGreen);
9
  TFieldType = (ftUndefined, ftFullSpace, ftEmpty, ftRed, ftYellow, ftGreen);
10
 
10
 
11
  TFieldProperties = record
11
  TFieldProperties = record
12
    Typ: TFieldType;
12
    Typ: TFieldType;
13
    Goal: Boolean;
13
    Goal: Boolean;
14
  end;
14
  end;
15
 
15
 
16
  TGameMode = (gmUndefined, gmNormal, gmDiagonal);
16
  TGameMode = (gmUndefined, gmNormal, gmDiagonal);
17
 
17
 
-
 
18
  TRow = record
-
 
19
    Indent: integer;
18
  TLevelArray = array of array of TFieldProperties;
20
    Fields: array of TFieldProperties;
-
 
21
  end;
-
 
22
  TLevelArray = array of TRow;
19
 
23
 
20
  TLevelError = (leNone, leInvalidElement, leEmptyBoard, leRowInvalidLength,
24
  TLevelError = (leUndefined, leNone, leInvalidElement, leEmptyBoard, leRowInvalidLength,
21
                 leUnsupportedVersion, leUnsupportedMode);
25
                 leUnsupportedVersion, leUnsupportedMode);
22
 
26
 
23
  TLevel = class(TObject)
27
  TLevel = class(TObject)
24
  private
28
  private
25
    FStringList: TStringList;
29
    FStringList: TStringList;
26
    procedure Load(ABoardFile: string);
30
    procedure Load(ABoardFile: string);
27
  public
31
  public
28
    constructor Create(ABoardFile: string);
32
    constructor Create(ABoardFile: string);
29
    destructor Destroy; override;
33
    destructor Destroy; override;
30
    function LevelStringToLevelArray(ShowErrors: boolean): TLevelArray;
34
    function LevelStringToLevelArray(ShowErrors: boolean): TLevelArray;
31
    function CheckLevelIntegrity: TLevelError; overload;
35
    function CheckLevelIntegrity: TLevelError; overload;
32
    function CheckLevelIntegrity(ShowErrors: boolean): TLevelError; overload;
36
    function CheckLevelIntegrity(ShowErrors: boolean): TLevelError; overload;
33
    function GetGameMode: TGameMode;
37
    function GetGameMode: TGameMode;
34
  end;
38
  end;
35
 
39
 
36
procedure DrawLevelPreview(Level: TLevel; Image: TImage; BackgroundColor: TColor);
40
procedure DrawLevelPreview(Level: TLevel; Image: TImage; BackgroundColor: TColor);
37
function FieldTypeWorth(t: TFieldType): integer;
41
function FieldTypeWorth(t: TFieldType): integer;
38
 
42
 
39
implementation
43
implementation
40
 
44
 
41
procedure DrawLevelPreview(Level: TLevel; Image: TImage; BackgroundColor: TColor);
45
procedure DrawLevelPreview(Level: TLevel; Image: TImage; BackgroundColor: TColor);
42
var
46
var
43
  LevelArray: TLevelArray;
47
  LevelArray: TLevelArray;
44
  y, x: integer;
48
  y, x: integer;
45
  t: TFieldType;
49
  t: TFieldType;
46
  halftabs: integer;
50
  indent: Integer;
47
const
51
const
48
  PREVIEW_BLOCK_SIZE = 10; // Enthält Field und Abstand
52
  PREVIEW_BLOCK_SIZE = 10; // Enthält Field und Abstand
49
  PREVIEW_TAB_SIZE = PREVIEW_BLOCK_SIZE div 2; // 5
53
  PREVIEW_TAB_SIZE = PREVIEW_BLOCK_SIZE div 2; // 5
50
begin
54
begin
51
  LevelArray := nil;
55
  LevelArray := nil;
52
 
56
 
53
  ClearImage(Image, BackgroundColor);
57
  ClearImage(Image, BackgroundColor);
54
 
58
 
55
  LevelArray := Level.LevelStringToLevelArray(false);
59
  LevelArray := Level.LevelStringToLevelArray(false);
56
 
60
 
57
  for y := Low(LevelArray) to High(LevelArray) do
61
  for y := Low(LevelArray) to High(LevelArray) do
58
  begin
62
  begin
59
    halftabs := 0;
-
 
60
    for x := Low(LevelArray[y]) to High(LevelArray[y]) do
63
    for x := Low(LevelArray[y].Fields) to High(LevelArray[y].Fields) do
61
    begin
64
    begin
62
      t := LevelArray[y][x].Typ;
65
      t      := LevelArray[y].Fields[x].Typ;
-
 
66
      indent := LevelArray[y].Indent;
63
 
67
 
64
      case t of
68
      case t of
65
        ftFullSpace: Image.Canvas.Brush.Color := BackgroundColor;
69
        ftFullSpace: Image.Canvas.Brush.Color := BackgroundColor;
66
        ftHalfSpace: begin
-
 
67
          Image.Canvas.Brush.Color := BackgroundColor;
-
 
68
          inc(halftabs);
-
 
69
        end;
-
 
70
        ftEmpty: Image.Canvas.Brush.Color := clWhite;
70
        ftEmpty:     Image.Canvas.Brush.Color := clWhite;
71
        ftGreen: Image.Canvas.Brush.Color := clLime;
71
        ftGreen:     Image.Canvas.Brush.Color := clLime;
72
        ftYellow: Image.Canvas.Brush.Color := clYellow;
72
        ftYellow:    Image.Canvas.Brush.Color := clYellow;
73
        ftRed: Image.Canvas.Brush.Color := clRed;
73
        ftRed:       Image.Canvas.Brush.Color := clRed;
74
      end;
74
      end;
75
 
75
 
76
      if LevelArray[y][x].Goal then
76
      if LevelArray[y].Fields[x].Goal then
77
        Image.Canvas.Pen.Color := clBlack
77
        Image.Canvas.Pen.Color := clBlack
78
      else
78
      else
79
        Image.Canvas.Pen.Color := BackgroundColor;
79
        Image.Canvas.Pen.Color := BackgroundColor;
80
 
80
 
81
      Image.Canvas.Rectangle((x-halftabs)*PREVIEW_BLOCK_SIZE + halftabs*PREVIEW_TAB_SIZE,
81
      Image.Canvas.Rectangle(x*PREVIEW_BLOCK_SIZE + indent*PREVIEW_TAB_SIZE,
82
                             y*PREVIEW_BLOCK_SIZE,
82
                             y*PREVIEW_BLOCK_SIZE,
83
                             (x-halftabs)*PREVIEW_BLOCK_SIZE + halftabs*PREVIEW_TAB_SIZE + PREVIEW_BLOCK_SIZE,
83
                             x*PREVIEW_BLOCK_SIZE + indent*PREVIEW_TAB_SIZE + PREVIEW_BLOCK_SIZE,
84
                             y*PREVIEW_BLOCK_SIZE                                        + PREVIEW_BLOCK_SIZE);
84
                             y*PREVIEW_BLOCK_SIZE                           + PREVIEW_BLOCK_SIZE);
85
    end;
85
    end;
86
  end;
86
  end;
87
end;
87
end;
88
 
88
 
89
function FieldTypeWorth(t: TFieldType): integer;
89
function FieldTypeWorth(t: TFieldType): integer;
90
begin
90
begin
91
  if t = ftGreen then result := 10
91
  if t = ftGreen then result := 10
92
  else if t = ftYellow then result := 20
92
  else if t = ftYellow then result := 20
93
  else if t = ftRed then result := 30
93
  else if t = ftRed then result := 30
94
  else result := 0;
94
  else result := 0;
95
end;
95
end;
96
 
96
 
97
{ TLevel }
97
{ TLevel }
98
 
98
 
99
const NUM_HEADERS = 2;
99
const NUM_HEADERS = 2;
100
 
100
 
101
constructor TLevel.Create(ABoardFile: string);
101
constructor TLevel.Create(ABoardFile: string);
102
begin
102
begin
103
  inherited Create;
103
  inherited Create;
104
  FStringList := TStringList.Create;
104
  FStringList := TStringList.Create;
105
  Load(ABoardFile);
105
  Load(ABoardFile);
106
end;
106
end;
107
 
107
 
108
destructor TLevel.Destroy;
108
destructor TLevel.Destroy;
109
begin
109
begin
110
  FreeAndNil(FStringList);
110
  FreeAndNil(FStringList);
111
 
111
 
112
  inherited;
112
  inherited;
113
end;
113
end;
114
 
114
 
115
function TLevel.GetGameMode: TGameMode;
115
function TLevel.GetGameMode: TGameMode;
116
begin
116
begin
117
  if LowerCase(FStringList.Strings[1]) = 'mode: normal' then
117
  if LowerCase(FStringList.Strings[1]) = 'mode: normal' then
118
    result := gmNormal
118
    result := gmNormal
119
  else if LowerCase(FStringList.Strings[1]) = 'mode: diagonal' then
119
  else if LowerCase(FStringList.Strings[1]) = 'mode: diagonal' then
120
    result := gmDiagonal
120
    result := gmDiagonal
121
  else
121
  else
122
    result := gmUndefined;
122
    result := gmUndefined;
123
end;
123
end;
124
 
124
 
125
procedure TLevel.Load(ABoardFile: string);
125
procedure TLevel.Load(ABoardFile: string);
126
var
126
var
127
  i: Integer;
127
  i: Integer;
128
begin
128
begin
129
  FStringList.Clear;
129
  FStringList.Clear;
130
  FStringList.LoadFromFile(ABoardFile);
130
  FStringList.LoadFromFile(ABoardFile);
131
 
131
 
132
  // Remove whitespaces and empty lines
132
  // Remove whitespaces and empty lines
133
  for i := FStringList.Count-1 downto NUM_HEADERS do
133
  for i := FStringList.Count-1 downto NUM_HEADERS do
134
  begin
134
  begin
135
    FStringList.Strings[i] := StringReplace(FStringList.Strings[i], ' ', '', [rfReplaceAll]);
135
    FStringList.Strings[i] := StringReplace(FStringList.Strings[i], ' ', '', [rfReplaceAll]);
136
    if FStringList.Strings[i] = '' then FStringList.Delete(i);
136
    if FStringList.Strings[i] = '' then FStringList.Delete(i);
137
  end;
137
  end;
138
end;
138
end;
139
 
139
 
-
 
140
function DotsAtBeginning(s: string): integer;
-
 
141
var
-
 
142
  i: integer;
-
 
143
begin
-
 
144
  result := 0;
-
 
145
  for i := 1 to Length(s) do
-
 
146
  begin
-
 
147
    if s[i] = '.' then
-
 
148
      Inc(result)
-
 
149
    else
-
 
150
      Exit;
-
 
151
  end;
-
 
152
end;
-
 
153
 
-
 
154
function DotsAtEnd(s: string): integer;
-
 
155
var
-
 
156
  i: integer;
-
 
157
begin
-
 
158
  result := 0;
-
 
159
  for i := Length(s) downto 1 do
-
 
160
  begin
-
 
161
    if s[i] = '.' then
-
 
162
      Inc(result)
-
 
163
    else
-
 
164
      Exit;
-
 
165
  end;
-
 
166
end;
-
 
167
 
140
function TLevel.LevelStringToLevelArray(ShowErrors: boolean): TLevelArray;
168
function TLevel.LevelStringToLevelArray(ShowErrors: boolean): TLevelArray;
141
var
169
var
142
  i: integer;
170
  i: integer;
143
  t: TFieldType;
171
  t: TFieldType;
144
  err: TLevelError;
172
  err: TLevelError;
145
  longestLine: Integer;
-
 
146
  thisLine: Integer;
-
 
147
  y: Integer;
173
  y: Integer;
148
  x: Integer;
174
  x: Integer;
149
  Line: string;
175
  Line: string;
150
  lch, uch: char;
176
  lch, uch: char;
151
  ch: char;
177
  ch: char;
152
begin
178
begin
153
  // Zuerst nach Fehlern suchen
179
  // Zuerst nach Fehlern suchen
154
  err := CheckLevelIntegrity(ShowErrors);
180
  err := CheckLevelIntegrity(ShowErrors);
155
  if err <> leNone then exit;
181
  if err <> leNone then exit;
156
 
182
 
157
  // Längste Zeile finden
-
 
158
  longestLine := 0;
-
 
159
  for i := NUM_HEADERS to FStringList.Count-1 do
-
 
160
  begin
-
 
161
    longestLine := Max(longestLine, Length(FStringList.Strings[i]));
-
 
162
  end;
-
 
163
 
-
 
164
  // Nun Matrix aufbauen
183
  // Nun Matrix aufbauen
165
  SetLength(result, 0);
184
  SetLength(result, 0);
166
  for i := NUM_HEADERS to FStringList.Count-1 do
185
  for i := NUM_HEADERS to FStringList.Count-1 do
167
  begin
186
  begin
168
    y := i - NUM_HEADERS;
187
    y := i - NUM_HEADERS;
169
 
188
 
170
    SetLength(result, Length(result)+1); // add line to matrix
189
    SetLength(result, Length(result)+1); // add line to matrix
171
    SetLength(result[y], longestLine);
-
 
172
 
190
 
173
    Line := FStringList.Strings[i];
191
    Line := FStringList.Strings[i];
-
 
192
    result[y].Indent := DotsAtBeginning(Line) - DotsAtEnd(Line);
-
 
193
    Line := StringReplace(Line, '.', '', [rfReplaceAll]);
-
 
194
    SetLength(result[y].Fields, Length(Line));
174
 
195
 
175
    for x := 0 to LongestLine-1 do
196
    for x := 0 to Length(Line)-1 do
176
    begin
197
    begin
177
      ch := Copy(Line,x+1,1)[1];
198
      ch := Line[x+1];
178
      lch := LowerCase(ch)[1];
199
      lch := LowerCase(ch)[1];
179
      uch := UpperCase(ch)[1];
200
      uch := UpperCase(ch)[1];
-
 
201
 
-
 
202
      t := ftUndefined;
180
      case lch of
203
      case lch of
181
        '*': t := ftFullSpace;
204
        '*': t := ftFullSpace;
182
        '.': t := ftHalfSpace;
-
 
183
        'e': t := ftEmpty;
205
        'e': t := ftEmpty;
184
        'r': t := ftRed;
206
        'r': t := ftRed;
185
        'y': t := ftYellow;
207
        'y': t := ftYellow;
186
        'g': t := ftGreen;
208
        'g': t := ftGreen;
187
      end;
209
      end;
188
 
210
 
189
      result[y][x].Typ := t;
211
      result[y].Fields[x].Typ := t;
190
      result[y][x].Goal := (ch = uch) and (ch <> lch);
212
      result[y].Fields[x].Goal := (ch = uch) and (ch <> lch);
191
    end;
213
    end;
192
  end;
214
  end;
193
end;
215
end;
194
 
216
 
195
function TLevel.CheckLevelIntegrity(ShowErrors: boolean): TLevelError;
217
function TLevel.CheckLevelIntegrity(ShowErrors: boolean): TLevelError;
196
resourcestring
218
resourcestring
197
  LNG_LVL_INVALID_ELEMENT = 'Level invalid: There are invalid elements in the file.'+#13#10#13#10+'Valid elements are r/R, y/Y, g/G, e/E, . and *.';
219
  LNG_LVL_INVALID_ELEMENT = 'Level invalid: There are invalid elements in the file.'+#13#10#13#10+'Valid elements are r/R, y/Y, g/G, e/E, . and *.';
198
  LNG_LVL_UNSUPPORTED_VERSION = 'Level format invalid: Version not supported.';
220
  LNG_LVL_UNSUPPORTED_VERSION = 'Level format invalid: Version not supported.';
199
  LNG_LVL_UNSUPPORTED_MODE = 'Level format invalid: Mode not supported.';
221
  LNG_LVL_UNSUPPORTED_MODE = 'Level format invalid: Mode not supported.';
200
  LNG_LVL_EMPTY_BOARD = 'Level invalid: Board is empty.';
222
  LNG_LVL_EMPTY_BOARD = 'Level invalid: Board is empty.';
201
  LNG_LVL_INVALID_LENGTH = 'Level invalid: Lines don''t have an equal amount of elements.';
223
  LNG_LVL_INVALID_LENGTH = 'Level invalid: Lines don''t have an equal amount of elements.';
202
begin
224
begin
203
  result := CheckLevelIntegrity;
225
  result := CheckLevelIntegrity;
204
  if ShowErrors then
226
  if ShowErrors then
205
  begin
227
  begin
206
    case result of
228
    case result of
207
      leNone: ;
229
      leNone: ;
208
      leInvalidElement: MessageDlg(LNG_LVL_INVALID_ELEMENT, mtError, [mbOk], 0);
230
      leInvalidElement: MessageDlg(LNG_LVL_INVALID_ELEMENT, mtError, [mbOk], 0);
209
      leUnsupportedVersion: MessageDlg(LNG_LVL_UNSUPPORTED_VERSION, mtError, [mbOk], 0);
231
      leUnsupportedVersion: MessageDlg(LNG_LVL_UNSUPPORTED_VERSION, mtError, [mbOk], 0);
210
      leUnsupportedMode: MessageDlg(LNG_LVL_UNSUPPORTED_MODE, mtError, [mbOk], 0);
232
      leUnsupportedMode: MessageDlg(LNG_LVL_UNSUPPORTED_MODE, mtError, [mbOk], 0);
211
      leEmptyBoard: MessageDlg(LNG_LVL_EMPTY_BOARD, mtError, [mbOk], 0);
233
      leEmptyBoard: MessageDlg(LNG_LVL_EMPTY_BOARD, mtError, [mbOk], 0);
212
      leRowInvalidLength: MessageDlg(LNG_LVL_INVALID_LENGTH, mtError, [mbOk], 0);
234
      leRowInvalidLength: MessageDlg(LNG_LVL_INVALID_LENGTH, mtError, [mbOk], 0);
213
    end;
235
    end;
214
  end;
236
  end;
215
end;
237
end;
216
 
238
 
217
function TLevel.CheckLevelIntegrity: TLevelError;
239
function TLevel.CheckLevelIntegrity: TLevelError;
218
var
240
var
219
  W: integer;
-
 
220
  H: extended;
-
 
221
  header, h_ver, h_dia, h_del, tmp: string;
-
 
222
  p: integer;
241
  tmp: string;
223
  i: Integer;
242
  i: Integer;
224
  Line: string;
243
  Line: string;
-
 
244
  firstLine: string;
-
 
245
  thisLine: string;
225
begin
246
begin
226
  result := leNone;
247
  result := leNone;
227
 
248
 
228
  // Check 1: Ist der Header OK?
249
  // Check 1: Ist der Header OK?
229
 
250
 
230
  if LowerCase(FStringList.Strings[0]) <> 'version 2' then
251
  if LowerCase(FStringList.Strings[0]) <> 'version 2' then
231
  begin
252
  begin
232
    result := leUnsupportedVersion;
253
    result := leUnsupportedVersion;
233
    exit;
254
    exit;
234
  end;
255
  end;
235
 
256
 
236
  if ((LowerCase(FStringList.Strings[1]) <> 'mode: normal') and (LowerCase(FStringList.Strings[1]) <> 'mode: diagonal')) then
257
  if ((LowerCase(FStringList.Strings[1]) <> 'mode: normal') and (LowerCase(FStringList.Strings[1]) <> 'mode: diagonal')) then
237
  begin
258
  begin
238
    result := leUnsupportedMode;
259
    result := leUnsupportedMode;
239
    exit;
260
    exit;
240
  end;
261
  end;
241
 
262
 
242
  // Check 2: Ist das Brett leer?
263
  // Check 2: Ist das Brett leer?
243
 
264
 
-
 
265
  tmp := '';
-
 
266
  for i := NUM_HEADERS to FStringList.Count-1 do tmp := tmp + FStringList.Strings[i];
244
  if FStringList.Count - NUM_HEADERS = 0 then
267
  if Trim(StringReplace(tmp, '.', '', [rfReplaceAll])) = '' then
245
  begin
268
  begin
246
    result := leEmptyBoard;
269
    result := leEmptyBoard;
247
    exit;
270
    exit;
248
  end;
271
  end;
249
 
272
 
250
  // Check 3: Geht das Level nicht in einem Quadrat oder Rechteck auf?
273
  // Check 3: Geht das Level nicht in einem Quadrat oder Rechteck auf?
251
 
274
 
-
 
275
  firstLine := StringReplace(FStringList.Strings[NUM_HEADERS], '.', '', [rfReplaceAll]);
252
  for i := NUM_HEADERS to FStringList.Count-1 do
276
  for i := NUM_HEADERS to FStringList.Count-1 do
253
  begin
277
  begin
-
 
278
    thisLine := StringReplace(FStringList.Strings[i], '.', '', [rfReplaceAll]);
254
    if Length(FStringList.Strings[i]) <> Length(FStringList.Strings[NUM_HEADERS]) then
279
    if Length(thisLine) <> Length(firstLine) then
255
    begin
280
    begin
256
      result := leRowInvalidLength; // at row y-NUM_HEADERS
281
      result := leRowInvalidLength; // at row y-NUM_HEADERS
257
      exit;
282
      exit;
258
    end;
283
    end;
259
  end;
284
  end;
260
 
285
 
261
  // Check 4: Gibt es ungültige Elemente in den Zeilen?
286
  // Check 4: Gibt es ungültige Elemente in den Zeilen?
262
 
287
 
263
  for i := NUM_HEADERS to FStringList.Count-1 do
288
  for i := NUM_HEADERS to FStringList.Count-1 do
264
  begin
289
  begin
265
    Line := FStringList.Strings[i];
290
    Line := FStringList.Strings[i];
266
 
291
 
267
    Line := StringReplace(Line, '.', '', [rfReplaceAll]);
292
    Line := StringReplace(Line, '.', '', [rfReplaceAll]);
268
    Line := StringReplace(Line, '*', '', [rfReplaceAll]);
293
    Line := StringReplace(Line, '*', '', [rfReplaceAll]);
269
    Line := StringReplace(Line, 'r', '', [rfReplaceAll, rfIgnoreCase]);
294
    Line := StringReplace(Line, 'r', '', [rfReplaceAll, rfIgnoreCase]);
270
    Line := StringReplace(Line, 'y', '', [rfReplaceAll, rfIgnoreCase]);
295
    Line := StringReplace(Line, 'y', '', [rfReplaceAll, rfIgnoreCase]);
271
    Line := StringReplace(Line, 'g', '', [rfReplaceAll, rfIgnoreCase]);
296
    Line := StringReplace(Line, 'g', '', [rfReplaceAll, rfIgnoreCase]);
272
    Line := StringReplace(Line, 'e', '', [rfReplaceAll, rfIgnoreCase]);
297
    Line := StringReplace(Line, 'e', '', [rfReplaceAll, rfIgnoreCase]);
273
 
298
 
274
    if Length(Line) > 0 then
299
    if Length(Line) > 0 then
275
    begin
300
    begin
276
      result := leInvalidElement; // at row y-NUM_HEADERS
301
      result := leInvalidElement; // at row y-NUM_HEADERS
277
      Exit;
302
      Exit;
278
    end;
303
    end;
279
  end;
304
  end;
280
 
305
 
281
  // Check 5: Kann im Level gesprungen werden
306
  // Check 5: Kann im Level gesprungen werden
282
 
307
 
283
  { Wird hier nicht abgeprüft, da dafür zuerst der PlayGround gebaut sein muss.
308
  { Wird hier nicht abgeprüft, da dafür zuerst der PlayGround gebaut sein muss.
284
    Es ist außerdem eher ein logischer Fehler, kein Fehler in der Levelstruktur! }
309
    Es ist außerdem eher ein logischer Fehler, kein Fehler in der Levelstruktur! }
285
end;
310
end;
286
 
311
 
287
end.
312
end.
288
 
313