Subversion Repositories jumper

Rev

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