Subversion Repositories jumper

Rev

Rev 9 | Rev 19 | 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
11 daniel-mar 9
  TFieldType = (ftFullSpace, ftHalfSpace, 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
 
18
  TLevelArray = array of array of TFieldProperties;
19
 
11 daniel-mar 20
  TLevelError = (leNone, leInvalidElement, leEmptyBoard, leRowInvalidLength,
21
                 leUnsupportedVersion, leUnsupportedMode);
1 daniel-mar 22
 
11 daniel-mar 23
  TLevel = class(TObject)
24
  private
25
    FStringList: TStringList;
26
    procedure Load(ABoardFile: string);
27
  public
28
    constructor Create(ABoardFile: string);
29
    destructor Destroy; override;
30
    function LevelStringToLevelArray(ShowErrors: boolean): TLevelArray;
31
    function CheckLevelIntegrity: TLevelError; overload;
32
    function CheckLevelIntegrity(ShowErrors: boolean): TLevelError; overload;
33
    function GetGameMode: TGameMode;
34
  end;
35
 
36
procedure DrawLevelPreview(Level: TLevel; Image: TImage; BackgroundColor: TColor);
8 daniel-mar 37
function FieldTypeWorth(t: TFieldType): integer;
1 daniel-mar 38
 
39
implementation
40
 
11 daniel-mar 41
procedure DrawLevelPreview(Level: TLevel; Image: TImage; BackgroundColor: TColor);
1 daniel-mar 42
var
43
  LevelArray: TLevelArray;
11 daniel-mar 44
  y, x: integer;
1 daniel-mar 45
  t: TFieldType;
46
  halftabs: integer;
11 daniel-mar 47
const
48
  PREVIEW_BLOCK_SIZE = 10; // Enthält Field und Abstand
49
  PREVIEW_TAB_SIZE = PREVIEW_BLOCK_SIZE div 2; // 5
1 daniel-mar 50
begin
51
  LevelArray := nil;
52
 
53
  ClearImage(Image, BackgroundColor);
54
 
11 daniel-mar 55
  LevelArray := Level.LevelStringToLevelArray(false);
1 daniel-mar 56
 
11 daniel-mar 57
  for y := Low(LevelArray) to High(LevelArray) do
1 daniel-mar 58
  begin
59
    halftabs := 0;
11 daniel-mar 60
    for x := Low(LevelArray[y]) to High(LevelArray[y]) do
1 daniel-mar 61
    begin
11 daniel-mar 62
      t := LevelArray[y][x].Typ;
1 daniel-mar 63
 
64
      case t of
11 daniel-mar 65
        ftFullSpace: Image.Canvas.Brush.Color := BackgroundColor;
66
        ftHalfSpace: begin
1 daniel-mar 67
          Image.Canvas.Brush.Color := BackgroundColor;
68
          inc(halftabs);
69
        end;
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;
74
      end;
75
 
11 daniel-mar 76
      if LevelArray[y][x].Goal then
1 daniel-mar 77
        Image.Canvas.Pen.Color := clBlack
78
      else
79
        Image.Canvas.Pen.Color := BackgroundColor;
80
 
11 daniel-mar 81
      Image.Canvas.Rectangle((x-halftabs)*PREVIEW_BLOCK_SIZE + halftabs*PREVIEW_TAB_SIZE,
82
                             y*PREVIEW_BLOCK_SIZE,
83
                             (x-halftabs)*PREVIEW_BLOCK_SIZE + halftabs*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
 
11 daniel-mar 140
function TLevel.LevelStringToLevelArray(ShowErrors: boolean): TLevelArray;
141
var
142
  i: integer;
143
  t: TFieldType;
144
  err: TLevelError;
145
  longestLine: Integer;
146
  thisLine: Integer;
147
  y: Integer;
148
  x: Integer;
149
  Line: string;
150
  lch, uch: char;
151
  ch: char;
152
begin
153
  // Zuerst nach Fehlern suchen
154
  err := CheckLevelIntegrity(ShowErrors);
155
  if err <> leNone then exit;
156
 
157
  // Längste Zeile finden
158
  longestLine := 0;
159
  for i := NUM_HEADERS to FStringList.Count-1 do
1 daniel-mar 160
  begin
11 daniel-mar 161
    longestLine := Max(longestLine, Length(FStringList.Strings[i]));
1 daniel-mar 162
  end;
163
 
11 daniel-mar 164
  // Nun Matrix aufbauen
165
  SetLength(result, 0);
166
  for i := NUM_HEADERS to FStringList.Count-1 do
1 daniel-mar 167
  begin
11 daniel-mar 168
    y := i - NUM_HEADERS;
1 daniel-mar 169
 
11 daniel-mar 170
    SetLength(result, Length(result)+1); // add line to matrix
171
    SetLength(result[y], longestLine);
1 daniel-mar 172
 
11 daniel-mar 173
    Line := FStringList.Strings[i];
1 daniel-mar 174
 
11 daniel-mar 175
    for x := 0 to LongestLine-1 do
176
    begin
177
      ch := Copy(Line,x+1,1)[1];
178
      lch := LowerCase(ch)[1];
179
      uch := UpperCase(ch)[1];
180
      case lch of
181
        '*': t := ftFullSpace;
182
        '.': t := ftHalfSpace;
183
        'e': t := ftEmpty;
184
        'r': t := ftRed;
185
        'y': t := ftYellow;
186
        'g': t := ftGreen;
187
      end;
1 daniel-mar 188
 
11 daniel-mar 189
      result[y][x].Typ := t;
190
      result[y][x].Goal := (ch = uch) and (ch <> lch);
191
    end;
192
  end;
193
end;
194
 
195
function TLevel.CheckLevelIntegrity(ShowErrors: boolean): TLevelError;
196
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 *.';
198
  LNG_LVL_UNSUPPORTED_VERSION = 'Level format invalid: Version not supported.';
199
  LNG_LVL_UNSUPPORTED_MODE = 'Level format invalid: Mode not supported.';
200
  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.';
202
begin
203
  result := CheckLevelIntegrity;
204
  if ShowErrors then
1 daniel-mar 205
  begin
11 daniel-mar 206
    case result of
207
      leNone: ;
208
      leInvalidElement: MessageDlg(LNG_LVL_INVALID_ELEMENT, mtError, [mbOk], 0);
209
      leUnsupportedVersion: MessageDlg(LNG_LVL_UNSUPPORTED_VERSION, mtError, [mbOk], 0);
210
      leUnsupportedMode: MessageDlg(LNG_LVL_UNSUPPORTED_MODE, mtError, [mbOk], 0);
211
      leEmptyBoard: MessageDlg(LNG_LVL_EMPTY_BOARD, mtError, [mbOk], 0);
212
      leRowInvalidLength: MessageDlg(LNG_LVL_INVALID_LENGTH, mtError, [mbOk], 0);
1 daniel-mar 213
    end;
214
  end;
11 daniel-mar 215
end;
1 daniel-mar 216
 
11 daniel-mar 217
function TLevel.CheckLevelIntegrity: TLevelError;
218
var
219
  W: integer;
220
  H: extended;
221
  header, h_ver, h_dia, h_del, tmp: string;
222
  p: integer;
223
  i: Integer;
224
  Line: string;
225
begin
226
  result := leNone;
1 daniel-mar 227
 
11 daniel-mar 228
  // Check 1: Ist der Header OK?
1 daniel-mar 229
 
11 daniel-mar 230
  if LowerCase(FStringList.Strings[0]) <> 'version 2' then
1 daniel-mar 231
  begin
11 daniel-mar 232
    result := leUnsupportedVersion;
233
    exit;
1 daniel-mar 234
  end;
235
 
11 daniel-mar 236
  if ((LowerCase(FStringList.Strings[1]) <> 'mode: normal') and (LowerCase(FStringList.Strings[1]) <> 'mode: diagonal')) then
1 daniel-mar 237
  begin
11 daniel-mar 238
    result := leUnsupportedMode;
239
    exit;
1 daniel-mar 240
  end;
241
 
11 daniel-mar 242
  // Check 2: Ist das Brett leer?
1 daniel-mar 243
 
11 daniel-mar 244
  if FStringList.Count - NUM_HEADERS = 0 then
1 daniel-mar 245
  begin
11 daniel-mar 246
    result := leEmptyBoard;
247
    exit;
1 daniel-mar 248
  end;
249
 
11 daniel-mar 250
  // Check 3: Geht das Level nicht in einem Quadrat oder Rechteck auf?
1 daniel-mar 251
 
11 daniel-mar 252
  for i := NUM_HEADERS to FStringList.Count-1 do
1 daniel-mar 253
  begin
11 daniel-mar 254
    if Length(FStringList.Strings[i]) <> Length(FStringList.Strings[NUM_HEADERS]) then
255
    begin
256
      result := leRowInvalidLength; // at row y-NUM_HEADERS
257
      exit;
258
    end;
1 daniel-mar 259
  end;
260
 
11 daniel-mar 261
  // Check 4: Gibt es ungültige Elemente in den Zeilen?
1 daniel-mar 262
 
11 daniel-mar 263
  for i := NUM_HEADERS to FStringList.Count-1 do
264
  begin
265
    Line := FStringList.Strings[i];
1 daniel-mar 266
 
11 daniel-mar 267
    Line := StringReplace(Line, '.', '', [rfReplaceAll]);
268
    Line := StringReplace(Line, '*', '', [rfReplaceAll]);
269
    Line := StringReplace(Line, 'r', '', [rfReplaceAll, rfIgnoreCase]);
270
    Line := StringReplace(Line, 'y', '', [rfReplaceAll, rfIgnoreCase]);
271
    Line := StringReplace(Line, 'g', '', [rfReplaceAll, rfIgnoreCase]);
272
    Line := StringReplace(Line, 'e', '', [rfReplaceAll, rfIgnoreCase]);
1 daniel-mar 273
 
11 daniel-mar 274
    if Length(Line) > 0 then
1 daniel-mar 275
    begin
11 daniel-mar 276
      result := leInvalidElement; // at row y-NUM_HEADERS
277
      Exit;
1 daniel-mar 278
    end;
279
  end;
280
 
11 daniel-mar 281
  // Check 5: Kann im Level gesprungen werden
282
 
283
  { 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! }
8 daniel-mar 285
end;
286
 
1 daniel-mar 287
end.