diff --git a/boulderman.lpr b/boulderman.lpr new file mode 100644 index 0000000..5ce4f79 --- /dev/null +++ b/boulderman.lpr @@ -0,0 +1,61 @@ + +Program Boulderman; + +Uses +crt, +Math, +gameUnit, +dataUnit; + +Var + filePath: String; + levelID: String; + theGS: GameState; + valid: boolean; + levelFound: boolean = true; +Begin + clrscr; + writeLn('<<< Boulderman >>'); + If paramCount < 1 Then + Begin + filePath := './intro.kye'; + writeLn('This is the introductory level set.'); + writeLn('Play another level set by calling the game'); + writeLn('with the path to the new level set file.'); + End + Else + filePath := paramStr(1); + Repeat + writeLn('--- Menu ---'); + writeLn('p -> Play'); + writeLn('j -> Jump to level'); + writeLn('q -> Quit'); + repeat + valid := true; + Case readKey Of + 'p': levelID := ''; + 'j': Begin + writeLn('What is the level name?'); + readLn(levelID); + End; + 'q': Exit; + else valid := false; + End; + until valid; + Repeat + theGS := readLevel(filePath, UpCase(levelID), levelFound); + If levelFound then + begin + playLevel(theGS); + levelID := theGS.next; + end + else + begin + writeLn('!!Level not found.'); + writeLn('Press ENTER to contiue.'); + readLn; + end; + Until (not levelFound) or (theGS.goal <> 0) or (length(levelID) = 0); + clrscr; + Until false; +End. diff --git a/gameUnit.pas b/gameUnit.pas new file mode 100644 index 0000000..6b1eebc --- /dev/null +++ b/gameUnit.pas @@ -0,0 +1,80 @@ +Unit GameUnit; + +Interface + +Uses +dataUnit; + +Procedure playLevel(var theGS: dataUnit.GameState); + +Implementation + +Uses +crt, +Math, +heroEnemy, +toolsProc, +moversProc; + +Procedure writeMap(Var theGS: dataUnit.GameState); + +Var + col: integer; + row: integer; +Begin + clrscr; + For row := 1 To theGS.mapHeight Do + Begin + For col := 1 To theGS.mapWidth Do + Write(theGS.map[row, col]); + writeln; + End; +End; + +Procedure playLevel(var theGS: dataUnit.GameState); + +Var + move: boolean; +Begin + randomize; + + (* Game loop *) + Repeat + writeMap(theGS); + gotoxy(1, theGS.mapHeight + 1); + writeLn(theGS.ID,' Hint:', theGS.clue); + gotoxy(theGS.manCol, theGS.manRow); + (* Player turn *) + Repeat + move := True; + Case readKey Of + 'i': moveMan(theGS, -1, 0); + 'j': moveMan(theGS, 0, -1); + 'k': moveMan(theGS, 1, 0); + 'l': moveMan(theGS, 0, 1); + ' ': ; + 'q': theGS.goal := -1; + Else + move := False; + End; + Until move; + (* Update map *) + doMagnet(theGS); + moveMovers(theGS); + moveEnemies(theGS); + Until theGS.goal <=0; + (* Win/Lose state *) + writeMap(theGS); + gotoxy(theGS.mapWidth Div 2 - 4, theGS.mapHeight Div 2); + If theGS.goal = 0 Then + Begin + Write('You Win!'); + End + Else + Write('You Lose!'); + gotoxy(1, theGS.mapHeight + 2); + writeLn('Press ENTER to continue.'); + readLn; +End; + +End. diff --git a/intro.kye b/intro.kye new file mode 100644 index 0000000..ddb3d0d --- /dev/null +++ b/intro.kye @@ -0,0 +1,139 @@ +6 +LOGO +Take the diamond +Introductory levels follow. Use File > Open to open alternative levels. +555555555555555555555555555555 +5 5 +5 5 +5 5 +5 5 +5 e 5 +5 e 5 +5 e 5 +5 e e 5 +5 e e 5 +5 e e e e eee 5 +5 ee e e e e 5 +5 e e e e eeeee 5 +5 e e ee ee e 5 +5 e e eeee eeee K* 5 +5 e 5 +5 e e 5 +5 eee 5 +5 5 +555555555555555555555555555555 +ROCKIES +Clear space before the rockfall +Now an element of danger... +555555555555555555555555555555 +5 5*vvvvv*5*vvvvv*5 5 +5 5vvvvvvv5vvvvvvv5 5 +5 5vvvvvvv5vvvvvvv5 5 +5555555vvvvvvv5vvvvvvv55555555 +5 59vvvvv759vvvvv75 5 +5 559vvv75559vvv755 5 +5555555556e4555556e45555555555 +5 5 5 5 +5 5veeevevKeveeeve5 5 +5555555eeveeeeveeeveee55555555 +5 5ee*eveeeeev*eee5 5 +5 5eeeeeeevveveeee5 5 +5555555eeveveeeeeeevee55555555 +5 5eeee*eee*veeeee5 5 +5 5eeeveeeeeeeevee5 5 +5555555evveeeevveeeeee55555555 +5 5e*eeeeee*eeeeee5 5 +5 5eeeeeeeeeeeeeee5 5 +555555555555555555555555555555 +PACMAN +Avoid the monsters +Easy so far +555555555555555555555555555555 +5 5 +5 K 5 +5 7555555556 7555559 756 5 +5 5 * 5* *5 5* 5 +5 5 5 5 5 5 +5* 5 8 4555553 8 2 5 8 5 +5 5 5 5 5 5 5 +5 5 5 5 5 5 5 +5 2 5 8 45555555 4553 5 5 +5 5 5 5 *5 5 5 +5 5 5 [ C 5 2 5 5 +5 8 5 5 E 5 2 5 +5 5 5 15555556 2 8 5 +5 5 5 *5 5 * * 5 +5 5 *5 5 5 5 +5 1555556 1556 7553 456 5 +5 * 5* 5 +5 *5 5 +555555555555555555555555555555 +VAULTS +Open doors with a magnet +Now for some basic mechanics +555555555555555555555555555555 +5 5* *5 5 +5 5 vvv 5 5 +5 5 *8b8* 5 5 +5 1553b1553 5 +5 5 +5 5 +5 5 +5 79b79 5 +5 K 53b15 5 +5 s 5^^^5 5 +5 5^^^5 5 +5 756b455***556b459 5 +5 5*****5***5*****5 5 +5 15555555555555553 5 +5 5 +5 5 +5 5 +5 5 +555555555555555555555555555555 +FLOW +Go with the flow. +Finally, some basic puzzles. +555555555555555555555555555555 +55a555555555555555555555555555 +55 r r a5 +55 7559*75559 7555559*7559 55 +55 5555555555 555555555555d55 +55u15 55 55 55 55 +55 *5 55K 55 53 55 +55 75 55 55 5* 55 +55 55 55 55 59d55 +55 15 5555555555555555 55 +55 *5 1555c D155553 55 +55 75 55 756 l 55 +55u55 55d5c 755555a55 +55 55 55 13 4555555555 +55 55 55 r a5 +55 5555555555555c55c559*759 55 +55 13*155553*15555553*15553 55 +5a l l 55 +555555555555555555555555555a55 +555555555555555555555555555555 +PUZZLER +Use the free turning block. +Go to File > Open and select another level to play +555555555555555555555555555555 +5 5 * 5* *5 5 +5 5 5 5 5 +5 K 5vvvvvvv5 b5 5 +5 s 59vvvvv75 T b 5 5 +5 559vvv755 b 5 5 +5555559 8 75556e4555bbb 5 5 +5*rrra5 5 5 5 5 5 +5 d55 5 5 5 5 5 +5 d55 5 5 ~ b5 5 5 +5 d15 5 5 b 5 5 5 +5 L d 5 5 5 b 5vvvvvv5 5 +55556 2 5B5 b 59vvvvv5 5 +5 e 5v5bbbbb 559vvv75 5 +556 459 5v5 b 5556e453 5 +5b b 5 5v5 b 5 5 +5 b b5 5v5 b 2 c c5 +5 b b 5 5v2 b e 755555 +5* 5 5ve b [ 75555555555 +555555555555555555555555555555 diff --git a/moversProc.pas b/moversProc.pas new file mode 100644 index 0000000..310c565 --- /dev/null +++ b/moversProc.pas @@ -0,0 +1,151 @@ +unit moversProc; + +interface + +uses dataUnit; + +procedure moveMovers(var g: GameState); + +implementation + +function turnMover(c: char; d: integer): char; +const + period = ord(high(TMoverDir))+1; +var + j, x: integer; + i :TMoverType; +begin + for i := low(TMoverType) to high(TMoverType) do + for j := ord(low(TMoverDir)) to ord(high(TMoverDir)) do + if c = movers[i, TMoverDir(j)] then + begin + x := (j+d) mod period; + Exit(movers[i, TMoverDir(x)]); + end; + Exit(c); +end; + +procedure rotateStep(var rstep, cstep: integer; clockWise: boolean); +begin + cstep := cstep + rstep; + rstep := cstep - rstep; + cstep := cstep - rstep; + if clockWise then + cstep := -cstep + else + rstep := -rstep; +end; + +procedure moveMover(var g: GameState; row, col, rstep, cstep: integer); +var + rns : integer = 0; + cns : integer = 0; +begin + + (* I don't move if I'm cought by a magnet *) + If (rstep <> 0) and (g.map[row - rstep, col] = 'Z') then Exit; + If (cstep <> 0) and (g.map[row, col - cstep] = 'N') then Exit; + + (* I don't move beyond the map *) + if (col + cstep > g.mapWidth) or (col + cstep < 1) or + (row + rstep > g.mapHeight) or (row + rstep < 1) then + begin + if g.map[row,col] in pushers then + g.map[row, col] := turnMover(g.map[row,col], 2); + Exit; + end; + + (* Turners turn me *) + case g.map[row + rstep, col + cstep] of + 'a': + begin + g.map[row, col] := turnMover(g.map[row,col], 1); + rotateStep(rstep, cstep, True); + end; + 'c': + begin + g.map[row, col] := turnMover(g.map[row,col], -1); + rotateStep(rstep, cstep, False); + end; + end; + + (* Simple movement *) + if g.map[row + rstep, col + cstep] = empty then + begin + g.map[row + rstep, col + cstep] := g.map[row,col]; + g.map[row, col] := empty; + Exit; + end; + + + (* Pushers turn around and push pushables *) + if (g.map[row,col] in pushers) then + begin + if (g.map[row + rstep, col + cstep] in pushable) and + (g.map[row + 2 * rstep, col + 2 * cstep] = empty) then + begin + g.map[row + 2 * rstep, col + 2 * cstep] := g.map[row + rstep, col + cstep]; + g.map[row + rstep, col + cstep] := empty; + end; + g.map[row, col] := turnMover(g.map[row,col], 2); + Exit; + end; + + (* Gliders slip on slipery things*) + if (g.map[row,col] in gliders) and + (g.map[row + rstep, col + cstep] in slipery) then + begin + if cstep <> 0 then + rns := 2 * random(1) - 1 + else + cns := 2 * random(1) - 1; + + if (g.map[row + rns, col + cns] = empty) and + (g.map[row + rstep + rns, col + cstep + cns] = empty) then + begin + g.map[row + rstep + rns, col + cstep + cns] := g.map[row,col]; + g.map[row, col] := empty; + end + else if (g.map[row - rns, col - cns] = empty) and + (g.map[row + rstep - rns, col + cstep - cns] = empty) then + begin + g.map[row + rstep - rns, col + cstep - cns] := g.map[row,col]; + g.map[row, col] := empty; + end; + end; +end; + +procedure moveMovers(var g: GameState); + +var + idx, row, col, Width, Height: integer; +begin + Width := g.mapWidth; + Height := g.mapHeight; + for idx := 0 to Height * Width - 1 do + begin + (* for up movers *) + row := idx div Width + 1; + col := idx mod Width + 1; + if (g.map[row, col] in UpMovers) then + moveMover(g, row, col, -1, 0); + (* for left movers *) + col := idx div Height + 1; + row := idx mod Height + 1; + if (g.map[row, col] in LeftMovers) then + moveMover(g, row, col, 0, -1); + (* for down movers *) + row := Height - idx div Width + 1; + col := Width - idx mod Width + 1; + if (g.map[row, col] in DownMovers) then + moveMover(g, row, col, 1, 0); + (* for Right movers *) + col := Width - idx div Height + 1; + row := Height - idx mod Height + 1; + if (g.map[row, col] in RightMovers) then + moveMover(g, row, col, 0, 1); + end; +end; + +end. + diff --git a/toolsProc.pas b/toolsProc.pas new file mode 100644 index 0000000..5f4094b --- /dev/null +++ b/toolsProc.pas @@ -0,0 +1,68 @@ + +Unit toolsProc; + +Interface + +Uses dataUnit; + +Procedure doMagnet(Var g:GameState); + +Implementation + +Function inBounds(g:GameState; row, col:integer): Boolean; +Begin + If (row <= g.mapHeight) And + (col <= g.mapWidth) And + (row >= 1) And + (col >= 1) Then + Exit(true) + Else + Exit(false); +End; + +Procedure doMagnet(Var g:GameState); + +Var + row, col, i, s: integer; + rstep: integer = 0; + cstep: integer = 0; +Begin + For row := 1 To g.mapHeight Do + For col := 1 To g.mapWidth Do + Begin + Case g.map[row,col] Of + 'N': cstep := 1; + 'Z': rstep := 1; + Else continue; + End; + For i := 0 To 1 Do + Begin + s := 2*i-1; + (* Follow man *) + If inBounds(g,row - 2*s*rstep, col - 2*s*cstep) And + (g.map[row - 2*s*rstep, col - 2*s*cstep] = man) And + (g.map[row - s*rstep, col - s*cstep] = empty) Then + Begin + g.map[row - s*rstep, col - s*cstep] := g.map[row,col]; + g.map[row,col] := empty; + (* Drag pushables *) + If inBounds(g, row + s*rstep, col + s*cstep) And + (g.map[row + s*rstep, col + s*cstep] In pushable) Then + Begin + g.map[row,col] := g.map[row + s*rstep, col + s*cstep]; + g.map[row + s*rstep, col + s*cstep] := empty; + End; + End; + (* Pull pushables *) + If inBounds(g, row + 2*s*rstep, col + 2*s*cstep) And + (g.map[row + 2*s*rstep, col + 2*s*cstep] In pushable) And + (g.map[row + s*rstep, col + s*cstep] = empty) Then + Begin + g.map[row + s*rstep, col + s*cstep] := g.map[row + 2*s*rstep, col + 2*s*cstep]; + g.map[row + 2*s*rstep, col + 2*s*cstep] := empty; + End; + End; + End; +End; + +End.