1
0
Fork 0

Added Turbo Pascal reimplementation of Ladder

This commit is contained in:
acn 2020-06-24 12:58:25 +02:00
parent 8139d51b26
commit 9649342652
22 changed files with 2049 additions and 1 deletions

411
LadderTP/LADACTOR.PAS Normal file
View File

@ -0,0 +1,411 @@
{
ReverseDirection makes the actor to go in the opposite direction,
it only works when the actor is moving left or right
}
PROCEDURE ReverseDirection(VAR a : ActorType);
BEGIN
CASE a.Dir OF
LEFT : a.Dir := RIGHT;
RIGHT : a.Dir := LEFT;
END;
END;
{
OnSolid returns true if standing on something solid i.e. Floor,
Disappearing floor or a Ladder
}
FUNCTION OnSolid(a : ActorType) : BOOLEAN;
BEGIN
OnSolid := (m.Field[a.Y + 1][a.X] IN ['=', '-', 'H', '|'])
OR (m.Field[a.Y][a.X] = 'H');
END;
FUNCTION EmptySpace(x, y : INTEGER) : BOOLEAN;
BEGIN
IF (x < 1) OR (x > LevelCols) THEN
EmptySpace := TRUE
ELSE
EmptySpace := NOT (m.Field[y][x] IN ['|', '=']);
END;
{
AboveLadder returns true when the actor is a above a Ladder
}
FUNCTION AboveLadder(a : ActorType) : BOOLEAN;
BEGIN
AboveLadder := m.Field[a.Y + 1][a.X] = 'H';
END;
{
OnEater returns true when the actor is standing on a Eater
}
FUNCTION OnEater(a : ActorType) : BOOLEAN;
BEGIN
OnEater := m.Field[a.Y][a.X] = '*';
END;
{
ClampToPlayfield makes sure that if the actor tries to walk or jump off
the playfield edges the actor stays inside the playfield and starts falling
}
PROCEDURE ClampToPlayfield(VAR a : ActorType);
BEGIN
IF a.Dir IN [LEFT, JUMPLEFT] THEN BEGIN
IF a.X < 1 THEN BEGIN
a.X := 1;
a.Dir := STOPPED;
a.DirRequest := NONE;
END;
END;
IF a.Dir IN [RIGHT, JUMPRIGHT] THEN BEGIN
IF a.X > LevelCols THEN BEGIN
a.X := LevelCols;
a.Dir := STOPPED;
a.DirRequest := NONE;
END;
END;
END;
{
InitActor set the fields of an Actor type to reasonable
initial values
}
PROCEDURE InitActor(VAR a : ActorType; t : KindType; xy : XYtype);
BEGIN
a.AType := t;
a.X := xy.x;
a.Y := xy.y;
a.Ch := 'X';
a.JumpStep := 0;
CASE t OF
ALAD:
BEGIN
a.Ch := 'g';
a.Dir := STOPPED;
a.DirRequest := NONE;
END;
AROCK:
BEGIN
a.Ch := 'o';
a.Dir := PENDING;
a.DirRequest := NONE;
END;
END;
END;
{
Set the Lad's character based on their current direction
}
PROCEDURE UpdateLadChar(VAR a : ActorType);
BEGIN
WITH a DO
CASE Dir OF
STOPPED: Ch := 'g';
RIGHT, JUMPRIGHT: Ch := 'p';
LEFT, JUMPLEFT: Ch := 'q';
FALLING: Ch := 'b';
UP, DOWN: Ch := 'p';
JUMP:
CASE a.DirRequest OF
NONE, STOPPED: Ch := 'g';
RIGHT, JUMPRIGHT: Ch := 'p';
LEFT, JUMPLEFT: Ch := 'q';
END;
END;
END;
{
Update an actor's direction to the latest's request
}
PROCEDURE UpdateDir(VAR a : ActorType);
BEGIN
a.Dir := a.DirRequest;
a.DirRequest := NONE;
END;
{
MoveActor handles the movements of an Actor
}
PROCEDURE MoveActor(VAR a : ActorType);
LABEL
loopAgain;
VAR
jd : ActionType;
i : INTEGER;
dispenser : DispenserPointerType;
BEGIN
loopAgain: { If just started falling we need to retest all conditions }
{ A STONE can only be LEFT/RIGHT/DOWN/FALLING or PENDING }
IF a.AType = AROCK THEN BEGIN
IF (a.Dir = PENDING) AND (a.DirRequest = NONE) THEN
EXIT;
{ If stopped select a random direction }
IF a.Dir = STOPPED THEN BEGIN
CASE Random(2) OF
0: IF (a.X > 1) THEN
IF EmptySpace(a.X - 1, a.Y) THEN
a.DirRequest := LEFT
ELSE
a.DirRequest := RIGHT
ELSE
a.DirRequest := RIGHT;
1: IF (a.X < LevelCols) THEN
IF EmptySpace(a.X + 1, a.Y) THEN
a.DirRequest := RIGHT
ELSE
a.DirRequest := LEFT
ELSE
a.DirRequest := LEFT;
END;
END;
{ Just reverse direction if at the playfield edge }
IF (a.X = 1) OR (a.X = LevelCols) THEN
ReverseDirection(a);
{ Start to fall if not on solid ground }
IF (a.Dir <> FALLING) AND NOT OnSolid(a) THEN
a.DirRequest := FALLING;
{ If Der rock just rolled over the top of a ladder then randomize direction }
IF AboveLadder(a) AND (a.Dir IN [LEFT, RIGHT]) THEN
CASE Random(4) OF
0: a.DirRequest := LEFT;
1: a.DirRequest := RIGHT;
ELSE a.DirRequest := DOWN;
END;
{ If on an Eater kill the stone }
IF OnEater(a) THEN BEGIN
dispenser := dispensers;
IF numDispensers > 1 THEN BEGIN
FOR i := 1 TO Random(numDispensers) DO
dispenser := dispenser^.next;
END;
InitActor(a, AROCK, dispenser^.xy);
m.AnyRocksPending := TRUE;
END;
END; { of stone only handling --------------------- }
{ If stopped or going left or going right and }
{ request to do something else, then try to do it }
IF (a.DirRequest <> NONE) THEN BEGIN
CASE a.Dir OF
STOPPED, PENDING:
IF a.DirRequest IN [LEFT, RIGHT, UP, DOWN, FALLING] THEN
UpdateDir(a);
JUMPUP:
IF a.DirRequest = LEFT THEN
a.Dir := JUMPLEFT
ELSE IF a.DirRequest = RIGHT THEN
a.Dir := JUMPRIGHT;
RIGHT:
IF a.DirRequest IN [LEFT, STOPPED] THEN
UpdateDir(a);
LEFT:
IF a.DirRequest IN [RIGHT, STOPPED] THEN
UpdateDir(a);
UP, DOWN:
IF a.DirRequest IN [STOPPED, UP, DOWN, RIGHT, LEFT] THEN
UpdateDir(a);
JUMPUP:
IF a.DirRequest = LEFT THEN
a.Dir := JUMPLEFT
ELSE
a.Dir := JUMPRIGHT;
JUMPRIGHT, JUMPLEFT:
IF a.DirRequest = STOPPED THEN
UpdateDir(a);
PENDING:
UpdateDir(a);
END;
END;
{ Handle starting of jumps }
IF (a.DirRequest = JUMP) THEN BEGIN
IF OnSolid(a) THEN BEGIN
CASE a.Dir OF
STOPPED, FALLING: BEGIN
a.DirRequest := STOPPED;
a.Dir := JUMPUP;
a.JumpStep := 1;
END;
LEFT: BEGIN
a.DirRequest := a.Dir;
a.Dir := JUMPLEFT;
a.JumpStep := 1;
END;
JUMPLEFT: BEGIN
a.DirRequest := LEFT;
a.Dir := JUMPLEFT;
a.JumpStep := 1;
END;
RIGHT: BEGIN
a.DirRequest := a.Dir;
a.Dir := JUMPRIGHT;
a.JumpStep := 1;
END;
JUMPRIGHT: BEGIN
a.DirRequest := RIGHT;
a.Dir := JumpRIGHT;
a.JumpStep := 1;
END;
END
END ELSE BEGIN
CASE a.Dir OF
JUMPUP, FALLING: a.DirRequest := STOPPED;
JUMPRIGHT: a.DirRequest := RIGHT;
JUMPLEFT: a.DirRequest := LEFT;
END;
END;
END ELSE IF (a.DirRequest = UP) AND (m.Field[a.Y][a.X] = 'H') THEN BEGIN
{ If at a ladder and want to go up }
a.Dir := UP;
a.DirRequest := NONE;
END ELSE IF(a.DirRequest = DOWN) AND
((m.Field[a.Y][a.X] = 'H') OR (m.Field[a.Y + 1][a.X] = 'H')) THEN BEGIN
a.Dir := DOWN;
a.DirRequest := NONE;
END;
CASE a.Dir OF
JUMPUP, JUMPLEFT, JUMPRIGHT: BEGIN
{ Do the jumping }
jd := jumpPaths[a.Dir, a.JumpStep];
IF (a.X + dirs[jd].x >= 1) AND (a.X + dirs[jd].x <= LevelCols) THEN BEGIN
CASE m.Field[a.Y + dirs[jd].y][a.X + dirs[jd].x] OF
'=', '|', '-' : BEGIN
{ If bumped into something try falling }
IF OnSolid(a) THEN BEGIN
a.Dir := a.DirRequest;
a.DirRequest := NONE;
END ELSE BEGIN
CASE a.Dir OF
JUMPUP: a.DirRequest := STOPPED;
JUMPRIGHT: a.DirRequest := RIGHT;
JUMPLEFT: a.DirRequest := LEFT;
END;
a.Dir := FALLING;
END;
a.JumpStep := 0;
END;
'H': BEGIN { Jumping onto a ladder }
a.Y := a.Y + dirs[jd].y;
a.X := a.X + dirs[jd].x;
a.Dir := STOPPED;
a.DirRequest := NONE;
a.JumpStep := 0;
END;
ELSE BEGIN
{ Just jumping in air }
a.X := a.X + dirs[jd].x;
a.Y := a.Y + dirs[jd].y;
a.JumpStep := SUCC(a.JumpStep);
IF (a.JumpStep > JumpsLen) THEN BEGIN
UpdateDir(a);
a.JumpStep := 0;
END ELSE IF (jumpPaths[a.Dir][a.JumpStep] = ACTIONEND) THEN BEGIN
UpdateDir(a);
a.JumpStep := 0;
END;
END;
END;
END ELSE BEGIN
IF OnSolid(a) THEN BEGIN
a.Dir := a.DirRequest;
a.DirRequest := NONE;
END ELSE BEGIN
a.Dir := FALLING;
a.DirRequest := STOPPED;
a.JumpStep := 0;
END;
END;
END;
STOPPED:
IF NOT OnSolid(a) THEN
a.Dir := FALLING;
FALLING: BEGIN
{ If falling then continue doing that until not in free space anymore, }
{ then continue the previous direction (if any) }
IF OnSolid(a) THEN
IF a.DirRequest = NONE THEN
a.DirRequest := STOPPED
ELSE
a.Dir := a.DirRequest
ELSE
a.Y := Succ(a.Y);
END;
UP:
{ Climb up until ladder is no more }
IF m.Field[a.Y - 1][a.X] IN ['H', '&', '$'] THEN
a.Y := Pred(a.Y)
ELSE
a.Dir := STOPPED;
DOWN:
{ Climb down until ladder is no more }
IF a.Dir = DOWN THEN
IF m.Field[a.Y + 1][a.X] IN ['H', '&', '$', ' ', '^', '.'] THEN
a.Y := Succ(a.Y)
ELSE
a.Dir := STOPPED;
LEFT: BEGIN
{ Stepped out into the void? Then start falling, }
{ but remember the previous direction }
IF NOT OnSolid(a) THEN BEGIN
a.DirRequest := a.Dir;
a.Dir := FALLING;
GOTO loopAgain;
END;
IF EmptySpace(a.X - 1, a.Y) THEN
a.X := Pred(a.X)
ELSE
a.DirRequest := STOPPED;
END;
RIGHT: BEGIN
{ Stepped out into the void? Then start falling, }
{ but remember the previous direction }
IF NOT OnSolid(a) THEN BEGIN
a.DirRequest := a.Dir;
a.Dir := FALLING;
GOTO loopAgain;
END;
IF EmptySpace(a.X + 1, a.Y) THEN
a.X := Succ(a.X)
ELSE
a.DirRequest := STOPPED;
END;
END;
{ Don't allow actor to end up outside of the playfield }
ClampToPlayfield(a);
IF a.AType = ALAD THEN
UpdateLadChar(a);
END;


BIN
LadderTP/LADCONF.COM Normal file

Binary file not shown.

20
LadderTP/LADCONST.PAS Normal file
View File

@ -0,0 +1,20 @@
CONST
Version = '1.33TP';
DataFileName = 'LADDER.DAT';
ConfigFileName = 'LADCONF.COM';
NumHighScores = 5; { # of stored high scores }
DataFileStrLength = 31; { # chars in data file terminal config strings }
DataFileNameLength = 29; { # chars in high score names }
CursOffStr = #$1B'[?25l'; { turn cursor off string }
CursOnStr = #$1B'[?25h'; { turn cursor on string }
NumPlaySpeeds = 5; { # of different playing speeds }
BonusTimeDecInterval = 3000; { decrement bonus time every 3 seconds }
NumLevels = 7; { # of distinct levels }
LevelRows = 20; { # of rows in a level map }
LevelCols = 79; { # of chars in a level map }
MaxDispensers = 3; { max # of rock dispensers on a level }
JumpsLen = 6; { max # of positions in a jump sequence }


BIN
LadderTP/LADDER.COM Normal file

Binary file not shown.

BIN
LadderTP/LADDER.DAT Normal file

Binary file not shown.

34
LadderTP/LADDER.PAS Normal file
View File

@ -0,0 +1,34 @@
{$C-}
{$I-}
{
Ladder: a reimplementation in Turbo Pascal of the classic
CP/M game "Ladder", originally written by Yahoo Software (not
Yahoo!).
Ladder is an ASCII character based platform arcade game similar to
Donkey Kong. You travel through levels with platforms and ladders
where rocks fall down from the top while you collect statues
before reaching the exit.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
}
PROGRAM ladder;
{$I LADCONST}
{$I LADTYPE}
{$I LADVAR}
{$I LADICONS}
{$I LADUTILS}
{$I LADFIELD}
{$I LADACTOR}
{$I LADPROCS}
{$I LADMAIN}


75
LadderTP/LADFIELD.PAS Normal file
View File

@ -0,0 +1,75 @@
{ LoadMap loads one of the playfields/maps into memory and also }
{ returns the coordinates of the initial Lad, and an array of }
{ coordinates where the dispensers are. }
PROCEDURE LoadMap(VAR lad : XYtype; VAR dispensers : DispenserPointerType; VAR numDispensers : INTEGER);
VAR
x, y : INTEGER;
newDispenser : DispenserPointerType;
dispenserPtr : DispenserPointerType;
rock1Ptr,rock2Ptr : RockPointerType;
BEGIN
{ get rid of anay previous rock dispensers }
WHILE dispensers <> NIL DO BEGIN
dispenserPtr := dispensers^.next;
DISPOSE(dispensers);
dispensers := dispenserPtr;
END;
IF m.Level > NumLevels THEN BEGIN
ClrScr;
WriteLN('Level ', m.Level, ' out of range');
CursOn;
Halt;
END;
WITH levels[m.Level] DO BEGIN
m.Name := Name;
m.InitialBonusTime := InitialBonusTime;
m.NumRocks := rocks;
{ dispose of any previous rocks }
WHILE m.Rocks <> NIL DO BEGIN
rock1Ptr := m.Rocks^.Next;
Dispose(m.Rocks);
m.Rocks := rock1Ptr;
END;
{ allocate new rocks }
FOR x := 1 TO Rocks DO BEGIN
rock1Ptr := m.Rocks;
New(rock2Ptr);
m.Rocks := rock2Ptr;
m.Rocks^.Next := rock1Ptr;
END;
m.AnyRocksPending := TRUE;
END;
{ Prepare the field to be loaded with a new level }
FOR y := 1 TO LevelRows DO
FOR x := 1 TO LevelCols DO
m.Field[y][x] := ' ';
dispensers := NIL;
numDispensers := 0;
FOR y := 1 TO LevelRows DO
FOR x := 1 TO LevelCols DO
CASE levels[m.Level].layout[y][x] OF
'p':
BEGIN
{ The lad will be put there by the rendered, so no need to have }
{ it on the map }
lad.x := x;
lad.y := y;
END;
'V':
BEGIN
m.Field[y][x] := 'V';
NEW(newDispenser);
newDispenser^.xy.x := x;
newDispenser^.xy.y := y;
newDispenser^.next := dispensers;
dispensers := newDispenser;
numDispensers := Succ(numDispensers);
END;
'.': { TODO - handle the rubber balls }
m.Field[y][x] := '.'
ELSE
m.Field[y][x] := levels[m.Level].layout[y][x];
END;
END;


244
LadderTP/LADICONS.PAS Normal file
View File

@ -0,0 +1,244 @@
{ Initialized constants for Ladder }
CONST
{
p - The place where the lad starts.
V - Der Dispenser. Der Rocks roll out of it to squash you flat.
* - Der Eaters. They eat the Der Rocks but oddly do not harm you in the slightest
= - Floor. You walk on it.
H - Ladder. You climb it.
| - Wall. You can't walk through it. You're not a ghost....yet.
. - Rubber Ball. It's very bouncy. This difference is, it bounces you.
$ - Treasure. The lad must get here to finish the level.
& - Gold Statue. Money!Money!Money!Money!Money!
^ - Fire. Turns you into extra crispy bacon.
- - Disposable Floor. Well, you can walk on it once.
}
levels : ARRAY[1..NumLevels] OF LevelType = (
(
Name : 'Easy Street';
InitialBonusTime : 35;
Rocks : 5;
Layout : (
' V $ ',
' H ',
' H H ',
' =========H================================================== ',
' H ',
' H ',
' H H H ',
'================H==========H================== ========H=====================',
' & H H | | ',
' H Easy Street ',
' H H ',
' =========H==========H========= ======================= ',
' H ',
' H ',
' H H ',
'======================== ====================== =========H============== ',
' H ',
' H ',
'* p H *',
'==============================================================================='
)
),
(
Name : 'Long Island';
InitialBonusTime : 45;
Rocks : 8;
Layout : (
' $ ',
' & H ',
' H |V V| H ',
'====H======================= ========================= ====================== ',
' H ',
' H ',
' H & | . . H ',
'========================== ====== =================== ===================H== ',
' H ',
' | H ',
' H | . . H ',
'====H===================== ====== ================ ====================== ',
' H ',
' H | ',
' H | . . H ',
'========================= ======== ============== ==================H== ',
' H ',
'============== | H ',
' Long Island | p * | * H ',
'==============================================================================='
)
),
(
Name : 'Ghost Town';
InitialBonusTime : 35;
Rocks : 5;
Layout : (
' V V V $ ',
' $$$ ',
' p H H $$$$$ H',
'==========H=== =H==============H',
' H H H',
' H & H H',
' ============== ==== = ====== = ==== =====H===== H',
' G ^^^ ^^^^^ ^^^^ ^^^^ ^^^ ^^^ $',
' h | ',
' o | H & | ',
' s ======================H============================== =========== ',
' t & H ',
' H ',
' | H H H ',
' T ==================H=================H===================H======= ',
' o H H ',
' w H ',
' n ^ H ',
'* ^^^ H *',
'==============================================================================='
)
),
(
Name : 'Tunnel Vision';
InitialBonusTime : 36;
Rocks : 5;
Layout : (
' V V ',
' ',
' H H | H ',
'=====H=====--======H========================== ===----====H=========== ',
' H H |&& H ',
' H H ================== H ',
' H H tunnel H H ',
' H =======---===----=================H= H H ',
' H | vision H H H ',
' H =========---& -----============H H H ',
' H H H | H H ',
' H H=========----===----================ H ==============',
' H & H ',
' H | H ',
'====---==== H | H ',
'| | ================---===---=================== H ',
'| === | H H p ',
'| $ | H ===H=======',
'|* $$$ *| * * * *H *H ',
'==============================================================================='
)
),
(
Name : 'Point of No Return';
InitialBonusTime : 35;
Rocks : 7;
Layout : (
' $ ',
' H V ',
' H ',
' HHHHHHHHHHHHH .HHHHHHHHHHHHHH H p ',
' & V H ==H==========',
' H H ',
' H H . H ',
'===H==============-----------============H==== H ',
' H H H ',
' H =====H============== ',
' H H H ',
' H &..^^^.....^..^ . ^^ H==--------- H ',
' H ============================H & H H ',
' H === === === H ---------=================H======',
' H H H ',
' H & H & H ',
' ==========-------------------------=======----------=================== ',
' ',
'^^^* ^^^^^^^^^^^^^^^^^^^^^^^^^* *^^^^^^^^^^*Point of No Return*^^^^',
'==============================================================================='
)
),
(
Name : 'Bug City';
InitialBonusTime : 37;
Rocks : 6;
Layout : (
' Bug City HHHHHHHH V ',
' HHH HHH ',
' H >mmmmmmmm ',
' H=============== ==================== H ',
' H |===== \ / V =====H==========',
' H \/ H ',
' H | $ H ',
' H H | H H ',
' H ====H======= p |&H H H ',
' H H ======================H ====== ',
' H H &| H H ',
' H H &| H H }{ =====H==== ',
'===H===& H =====================H H H ',
' H H H H ',
' H H & H ',
' ======H=== ======= H <> & H ',
' H========== ===== = ============',
' }i{ H ',
'* H *',
'==============================================================================='
)
),
(
Name : 'GangLand';
InitialBonusTime : 32;
Rocks : 6;
Layout : (
' =Gang Land= V ',
' == _ == . ',
' p H | [] |_| | & . H ',
'===========H | |_| | H === ===================H ',
' V H ============= H====== H ',
' H H & H ',
' H H | | H ',
' H H ^^^&&^^^ & ^ ^^^ H H | =============H ',
' H======H =======================H===========H===== & H ',
' H H H | &&& H ',
' H H H | &&&&& H ',
' H H H | =============H ',
' =====------================= H | $ $ ',
' | H | $$$ $$$ ',
'====------=== | H | $$$$$ $$$$$ ',
' | = | ============= ============ ',
' | $ ^ & ',
' |^^^^^^^^^^^^^^ $ ^ ====== ',
'* . & ^ H*^ ^ ^ ^^^^^^^^^^^^',
'==============================================================================='
)
)
);
{
A moving jump is UR/UR/R/R/DR/DR
or UL/UL/L/L/DL/DL
A standing jump is U/U/-/D/D
====================
----234-----23------
---1---5----14------
--0-----6---05------
====================
}
jumpPaths : Array[JUMPRIGHT..JUMPLEFT] OF ActionArrayType = (
(UPRIGHT, UPRIGHT, RIGHT, RIGHT, DOWNRIGHT, DOWNRIGHT),
(UP, UP, STOPPED, DOWN, DOWN, ACTIONEND),
(UPLEFT, UPLEFT, LEFT, LEFT, DOWNLEFT, DOWNLEFT)
);
dirs : ARRAY[STOPPED..JUMPLEFT] OF XYtype = (
(x: 0; y: 0), { STOPPED }
(x: 0; y:-1), { UP }
(x: 1; y:-1), { UPRIGHT }
(x: 1; y: 0), { RIGHT }
(x: 1; y: 1), { DOWNRIGHT }
(x: 0; y: 1), { DOWN }
(x:-1; y: 1), { DOWNLEFT }
(x:-1; y: 0), { LEFT }
(x:-1; y:-1), { UPLEFT }
(x: 0; y: 1), { FALLING }
(x: 0; y: 0), { JUMP }
(x: 0; y: 0), { JUMPRIGHT }
(x: 0; y: 0), { JUMPUP }
(x: 0; y: 0) { JUMPLEFT }
);
ReadmsWait : ARRAY [1..NumPlaySpeeds] OF INTEGER = (100, 50, 25, 13, 7);


221
LadderTP/LADMAIN.PAS Normal file
View File

@ -0,0 +1,221 @@
{
Note that this code makes free use of the dreaded GOTO command.
}
VAR
msecs, bonusTimeTicks, moveInterval, x, y : INTEGER;
ch : CHAR;
rockPtr : RockPointerType;
LABEL
restartGame, restartLevel, newLevel;
BEGIN { MAIN }
dispensers := NIL; { initialize our linked lists for dispenser }
m.Rocks := NIL; { and rocks }
lastScore := -1;
playSpeed := 1;
ReadDataFile;
restartGame:
m.LadsRemaining := 5;
m.Level := 1;
m.Score := 0;
levelCycle := 1;
displayLevel := 1;
nextNewLad := 100;
CursOn;
Randomize;
MainMenu;
moveInterval := ReadmsWait[playSpeed];
ClrScr;
CursOff;
newLevel:
{ Load a level and show it }
LoadMap(ladXY, dispensers, numDispensers);
restartLevel:
m.RemainingBonusTime := m.InitialBonusTime - 2 * (levelCycle - 1);
InitActor(lad, ALAD, ladXY);
DisperseRocks;
{ Show the initial map on screen }
DrawMap;
msecs := 0;
bonusTimeTicks := 0;
WHILE TRUE DO BEGIN
Delay(1);
msecs := Succ(msecs);
IF msecs >= moveInterval THEN BEGIN
{ move the Lad and rocks every X milliseconds }
msecs := 0;
bonusTimeTicks := bonusTimeTicks + moveInterval;
IF bonusTimeticks >= BonusTimeDecInterval THEN BEGIN
{ every 3 seconds, decrement the bonus time/time remaining value }
bonusTimeTicks := bonusTimeTicks - BonusTimeDecInterval;;
m.RemainingBonusTime := Pred(m.RemainingBonusTime);
GotoXY(74, 21);
Write(m.RemainingBonusTime : 2);
IF m.RemainingBonusTime <=0 THEN BEGIN
{ died: do the animation and check if we still have lives left }
IF LadDeath THEN
GOTO restartLevel;
GOTO restartGame;
END;
END;
IF m.AnyRocksPending THEN BEGIN
{ pending rocks? see if it's time to start moving }
rockPtr := m.Rocks;
m.AnyRocksPending := FALSE;
WHILE rockPtr <> NIL DO BEGIN
IF (rockPtr^.Dir = PENDING) THEN
IF (Random(10) = 0) THEN
rockPtr^.DirRequest := FALLING
ELSE
m.AnyRocksPending := TRUE;
rockPtr := rockPtr^.Next;
END;
END;
{ Move the lad according to players wishes }
IF (lad.Dir <> STOPPED) OR (lad.DirRequest <> NONE) THEN BEGIN
x := lad.X; y := lad.Y;
GotoXY(x, y); Write(m.Field[y][x]);
MoveActor(lad);
GotoXY(lad.X, lad.Y); Write(lad.Ch);
IF (x <> lad.X) AND (y = lad.Y) THEN BEGIN
{ get rid of disappearing flooring }
IF m.Field[y + 1][x] = '-' THEN BEGIN
m.Field[y + 1][x] := ' ';
GotoXY(x, y + 1); Write(' ');
END;
END;
CASE m.Field[lad.Y][lad.X] OF
'$' : BEGIN { at the Gold: level done }
UpdateScore(ScoreMoney);
displayLevel := Succ(displayLevel);
IF (m.Level > levelCycle) OR (m.Level = NumLevels) THEN BEGIN
{ done with current cycle, recycle to 1st level }
m.Level := 1;
levelCycle := SUCC(levelCycle);
END ELSE BEGIN
m.Level := Succ(m.Level);
END;
GOTO newLevel;
END;
'^': BEGIN { death by fire }
IF LadDeath THEN
GOTO restartLevel;
GOTO restartGame;
END;
'&': BEGIN
{ found a statue, adjust the score, remove the statue }
UpdateScore(ScoreStatue);
m.Field[lad.Y][lad.X] := ' ';
END;
'.': BEGIN {trampoline: choose a random direction }
CASE Random(5) OF
0: BEGIN
lad.Dir := LEFT;
lad.DirRequest := NONE;
lad.JumpStep := 0;
END;
1: BEGIN
lad.Dir := RIGHT;
lad.DirRequest := NONE;
lad.JumpStep := 0;
END;
2: BEGIN
lad.Dir := JUMPUP;
lad.DirRequest := STOPPED;
lad.JumpStep := 1;
END;
3: BEGIN
lad.Dir := JUMPLEFT;
lad.DirRequest := LEFT;
lad.JumpStep := 1;
END;
4: BEGIN
lad.Dir := JUMPRIGHT;
lad.DirRequest := RIGHT;
lad.JumpStep := 1;
END;
END;
END;
END;
END; { of Lad movement handler -------------------------- }
{ Move the rock(s) }
rockPtr := m.Rocks;
WHILE rockPtr <> NIL DO BEGIN
{ Don't draw the rock if it's pending }
IF rockPtr^.Dir <> PENDING THEN BEGIN
IF Collision(rockPtr) THEN
IF LadDeath THEN
GOTO restartLevel
ELSE
GOTO restartGame;
x := rockPtr^.X;
y := rockPtr^.Y;
GotoXY(x, y);
Write(m.Field[y][x]);
END;
MoveActor(rockPtr^);
{ Don't draw the rock if it's pending }
IF rockPtr^.Dir <> PENDING THEN BEGIN
IF Collision(rockPtr) THEN
IF LadDeath THEN
GOTO restartlevel
ELSE
GOTO restartGame;
GotoXY(rockPtr^.X, rockPtr^.Y);
Write(rockPtr^.Ch);
END;
rockPtr := rockPtr^.Next;
END; { of rock movement handler ------------------------- }
{ end of tick handler =================================== }
END;
IF KeyPressed THEN BEGIN
Read(Kbd, ch);
ch := UpCase(ch);
IF ch = leftKey THEN BEGIN
lad.DirRequest := LEFT
END ELSE IF ch = rightKey THEN BEGIN
lad.DirRequest := RIGHT
END ELSE IF ch = upKey THEN BEGIN
lad.DirRequest := UP
END ELSE IF ch = downKey THEN BEGIN
lad.DirRequest := DOWN
END ELSE IF ch = ' ' THEN BEGIN
lad.DirRequest := JUMP;
END ELSE IF ch = #$1B THEN BEGIN
CursOn;
GotoXY(1, 23); Write('Type RETURN to continue: ');
REPEAT
WHILE NOT KeyPressed DO;
Read(Kbd, ch);
UNTIL ch IN [#$0D, #$03];
IF ch = #$03 THEN { ^C goes back to main menu }
GOTO restartGame;
GotoXY(1, 23); ClrEOL;
CursOff;
END ELSE IF ch = #$03 THEN BEGIN
GOTO restartGame
END ELSE BEGIN
lad.DirRequest := STOPPED
END;
END; { of keypress handler ================================ }
END;
END.


393
LadderTP/LADPROCS.PAS Normal file
View File

@ -0,0 +1,393 @@
PROCEDURE Instructions;
BEGIN
ClrScr;
WriteLN;
WriteLN('You are a Lad trapped in a maze. Your mission is is to explore the');
WriteLN('dark corridors never before seen by human eyes and find hidden');
WriteLN('treasures and riches');
WriteLN;
WriteLN('You control Lad by typing the direction buttons and jumping by');
WriteLN('typing SPACE. But beware of the falaling rocks called Der rocks.');
WriteLN('You must find and grasp the treasures (shown as $) BEFORE the');
WriteLN('bonus time runs out.');
WriteLN;
WriteLN('A new Lad will be awarded for every 10,000 points.');
WriteLN('Extra points are awarded for touching the gold');
WriteLN('statues (shown as &). You will receive the bonus time points');
WriteLN('that are left when you have finished the level.');
WriteLN;
WriteLN('Type an ESCape to pause the egame.');
WriteLN;
WriteLN('Remember, there is more than one way to skin a cat. (Chum)');
WriteLN;
WriteLN('Good luck Lad.');
WriteLN;
WriteLN;
WriteLN;
Write('Type RETURN to return to main menu: ');
Read;
END;
PROCEDURE ConfigureLadder;
VAR
configPgm : FILE;
Ok : BOOLEAN;
BEGIN
{$I-}
Assign(configPgm, ConfigFileName);
WriteLN('Linking to configuration program');
Execute(configPgm);
Ok := IOresult = 0; { IOresult must be called after failed Execute }
{$I+}
WriteLN;
WriteLN('Unable to link to LADCONF.COM');
WriteLN('Ladder configuration program missing');
Halt;
END;
PROCEDURE MainMenu;
VAR
ch : CHAR;
insult : BOOLEAN;
i, msecs : INTEGER;
configPgm : FILE;
BEGIN
REPEAT
ClrScr;
WriteLN(' LL dd dd');
WriteLN(' LL dd dd tm');
WriteLN(' LL aaaa ddddd ddddd eeee rrrrrrr');
WriteLN(' LL aa aa dd dd dd dd ee ee rr rr');
WriteLN(' LL aa aa dd dd dd dd eeeeee rr');
WriteLN(' LL aa aa dd dd dd dd ee rr');
WriteLN(' LLLLLLLL aaa aa ddd dd ddd dd eeee rr');
WriteLN;
WriteLN(' Version : ', Version);
WriteLN('(c) 1982, 1983 Yahoo Software Terminal: ', dataFileContents.TerminalName);
WriteLN('10970 Ashton Ave. Suite 312 Play speed: ', playSpeed);
Write( 'Los Angeles, Ca 90024 ');
WriteLN('Up = ', upKey, ' Down = ',downKey ,' Left = ', leftKey, ' Right = ', rightKey);
WriteLN(' Jump = Space Stop = Other');
WriteLN;
WriteLN('P = Play game High Scores');
WriteLN('L = Change level of difficulty');
WriteLN('C = Configure Ladder');
WriteLN('I = Instructions');
WriteLN('E = Exit Ladder');
WriteLN;
WriteLN;
Write('Enter one of the above: ');
{ show high scores }
FOR i := 1 TO NumHighScores DO BEGIN
GotoXY(40, i + 15);
Write(i, ') ');
IF highScores[i].Score <> 0 THEN
Write(highScores[i].Score:4, '00 ', highScores[i].Name);
END;
IF lastScore <> -1 THEN BEGIN
GotoXY(40, 22);
Write('Last Score: ',lastScore,'00');
END;
GotoXY(25, 22);
{ randomly prompt the user to get a move on }
insult := FALSE;
msecs := 0;
REPEAT
Delay(1);
msecs := Succ(msecs);
IF msecs >= 1000 THEN BEGIN
msecs := 0;
IF insults THEN BEGIN
IF insult THEN BEGIN
GotoXY(1, 24);
ClrEol;
insult := FALSE;
GotoXY(25, 22);
END ELSE BEGIN
i := Random(10);
IF i > 7 THEN BEGIN
insult := TRUE;
GotoXY(1, 24);
IF i = 8 THEN
Write('You eat quiche!')
ELSE
Write('Come on, we don''t have all day!');
GotoXY(25, 22);
END;
END;
END;
END;
ch := #0;
IF KeyPressed THEN BEGIN
Read(Kbd, ch);
ch := UpCase(ch);
IF ch = 'L' THEN BEGIN { change playing speed }
playSpeed := SUCC(playSpeed MOD NumPlaySpeeds);
GotoXY(52, 11); Write(playSpeed);
GotoXY(25, 22);
END ELSE IF ch = 'I' THEN
Instructions;
END;
UNTIL ch IN ['P','C','I','E'];
UNTIL ch in ['P', 'C', 'E'];
IF ch = 'E' THEN BEGIN
Write('Exiting...');
GotoXY(1, 24);
ClrEOL;
GotoXY(1, 23);
Halt;
END ELSE IF ch = 'C' THEN BEGIN { run configuration program }
GotoXY(1, 23);
ConfigureLadder;
END;
END;
{
Read the LADDER.DAT file. This is the same file used in the original
game. We don't use the cursor control stuff (that's handled by Turbo
Pascal, but do use the control keys, flags and high scores.
I'm sure this code could be better, but it does the job.
}
PROCEDURE ReadDataFile;
VAR
dataFile,configPgm : FILE;
i, j : INTEGER;
BEGIN
{$I-}
Assign(dataFile, DataFileName);
Reset(dataFile);
IF IOresult <> 0 THEN BEGIN
ConfigureLadder;
END;
BlockRead(dataFile, dataFileContents, SizeOf(dataFileContents) DIV 128);
IF IOresult <> 0 THEN BEGIN
WriteLN('Ladder not configred');
WriteLN;
ConfigureLadder;
END;
WITH dataFileContents DO BEGIN
sound := Flags[0] = 'Y';
insults := Flags[1] = 'Y';
downKey := Keys[0];
leftKey := Keys[1];
rightKey := Keys[2];
upKey := Keys[3];
FOR i := 1 TO NumHighScores DO BEGIN
highScores[i].Name[0] := CHAR(Highs[i][0]);
IF Highs[i][0] = 0 THEN
highScores[i].Score := 0
ELSE
highScores[i].Score := Highs[i][1] OR (Highs[i][2] SHL 8);
FOR j := 1 TO Highs[i][0] DO
highScores[i].Name[j] := CHAR(Highs[i][j + 2]);
END;
END;
Close(dataFile);
{$I+}
END;
{
Update the high scores in LADDER.DAT. See the comments in
ReadDataFile.
}
PROCEDURE WriteDataFile;
VAR
dataFile : FILE;
i, j : INTEGER;
BEGIN
{$I-}
Assign(dataFile, DataFileName);
Reset(dataFile);
IF IOresult <> 0 THEN BEGIN
WriteLN('Reset failed on LADDER.DAT');
Halt;
END;
WITH dataFileContents DO BEGIN
FOR i := 1 TO NumHighScores DO BEGIN
Highs[i][0] := Length(highScores[i].Name);
Highs[i][1] := Lo(highScores[i].Score);
Highs[i][2] := Hi(highScores[i].Score);
FOR j := 1 TO Highs[i][0] DO
Highs[i][j + 2] := ORD(highScores[i].Name[j]);
END;
BlockWrite(dataFile, dataFileContents, SizeOf(dataFileContents) DIV 128);
IF IOresult <> 0 THEN BEGIN
WriteLN('BlockWrite failed on LADDER.DAT');
Halt;
END;
END;
Close(dataFile);
{$I+}
END;
{
kill the lad off in a horrible death of mixed up characters.
}
FUNCTION LadDeath : BOOLEAN;
CONST
NumSymbols = 11;
symbols : ARRAY[1..NumSymbols] OF CHAR = ('p', 'b', 'd', 'q', 'p', 'b', 'd', 'q', '-', '-', '_');
VAR
i, j : INTEGER;
name: STRING[DataFileNameLength];
ch : CHAR;
BEGIN
FOR i := 1 TO NumSymbols DO BEGIN
Beep;
GotoXY(lad.X, lad.Y); Write(symbols[i]);
Delay(150);
END;
m.LadsRemaining := Pred(m.LadsRemaining);
GotoXY(8, 21); Write(m.ladsRemaining : 2);
LadDeath := m.LadsRemaining > 0;
IF m.LadsRemaining <= 0 THEN BEGIN
FOR i := 1 TO NumHighScores DO BEGIN
WriteLN(highScores[i].Score);
IF m.Score >= highScores[i].Score THEN BEGIN
FOR j := NumHighScores - 1 DOWNTO i DO BEGIN
highScores[j + 1] := highScores[j];
END;
ClrScr;
GotoXY(10, 7);
FOR j := 1 TO 7 DO
Write('YAHOO! ');
WriteLN;
WriteLN;
CASE levelCycle OF
1 : WriteLN('You really don''t deserve this but...');
2 : WriteLN('Not bad for a young Lad');
3 : WriteLN('Amazing! You rate!!');
4 : WriteLN('Looks like we have a Lad-Der here');
5 : WriteLN('Yeah! Now you are a Lad-Wiz!');
6 : WriteLN('Wow! You are now a Lad-Guru!');
ELSE WriteLN('You are a true Lad-Master!!!');
END;
WriteLN;
While KeyPressed DO
Read(Kbd, ch);
Write('Enter your name: ');
CursOn;
Read(name);
CursOff;
GotoXY(1, 17);
Write('Updating high scores...');
highScores[i].Score := m.Score;
highScores[i].Name := name;
WriteDataFile;
EXIT;
END;
END;
END;
END;
PROCEDURE DrawMap;
VAR
x, y : INTEGER;
ch : CHAR;
BEGIN
FOR y := 1 TO LevelRows DO BEGIN
GotoXY(1, y);
FOR x := 1 TO LevelCols DO
Write(m.Field[y][x]);
END;
{ Draw the lad at rest }
GotoXY(lad.X, lad.Y); Write('g');
{ Initialize the entire status line }
GotoXY(1, 21);
Write('Lads ',m.LadsRemaining : 2,
' Level ', displayLevel : 2,
' Score ', m.Score : 5, '00',
' Bonus time ', m.RemainingBonusTime : 2, '00');
GotoXY(1, 23); Write('Get ready!');
Delay(1000);
WHILE KeyPressed DO
Read(Kbd, ch);
GotoXY(1, 23); Write(' ');
END;
{
Adjusts the score according to some event.
}
PROCEDURE UpdateScore(scoreEvent : ScoreType);
BEGIN
CASE scoreEvent OF
ScoreStatue: BEGIN
m.Score := m.Score + m.RemainingBonusTime;
Beep;
END;
ScoreReset: m.Score := 0;
ScoreRock: BEGIN
m.Score := m.Score + 2;
Beep;
END;
ScoreMoney: BEGIN
WHILE m.RemainingBonusTime > 0 DO BEGIN
GotoXY(1, 23); Write('Hooka!'); Beep; Delay(100);
GotoXY(1, 23); Write(' '); Delay(100);
m.Score := Succ(m.Score);
m.RemainingBonusTime := Pred(m.RemainingBonusTime);
GotoXY(36, 21); Write(m.Score : 5);
GotoXY(74, 21); Write(m.RemainingBonusTime : 2);
END;
END;
END;
{ give a new lad if over 10,000 points }
IF m.Score >= nextNewLad THEN BEGIN
m.ladsRemaining := Succ(m.ladsRemaining);
nextNewLad := nextNewLad + 100;
GotoXY(8, 21); Write(m.ladsRemaining : 2);
END;
GotoXY(36, 21);
Write(m.Score : 5);
END;
{
Check to see if the Lad has collided with, or jumped over a rock.
}
FUNCTION Collision(rockPtr : RockPointerType) : BOOLEAN;
BEGIN
Collision := FALSE;
IF lad.X = rockPtr^.X THEN BEGIN
IF lad.Y = rockPtr^.Y THEN BEGIN
Collision := TRUE;
END ELSE IF (lad.Y = rockPtr^.Y - 1) AND
(m.Field[lad.Y + 1][lad.X] = ' ') THEN BEGIN
{ score for jumping rocks }
UpdateScore(ScoreRock);
END ELSE IF (lad.Y = rockPtr^.Y - 2) THEN
IF (m.Field[lad.Y + 1][lad.X] = ' ') AND (m.Field[lad.Y + 2][lad.X] = ' ') THEN BEGIN
{ score for jumping rocks }
UpdateScore(ScoreRock);
END;
END;
END;
{
Set each rock up in a random dispenser.
}
PROCEDURE DisperseRocks;
VAR
rockPtr : RockPointerType;
dispenserPtr : DispenserPointerType;
i : INTEGER;
BEGIN
rockPtr := m.Rocks;
WHILE rockPtr <> NIL DO BEGIN
dispenserPtr := dispensers;
IF numDispensers > 1 THEN
FOR i := 1 TO Random(numDispensers) DO
dispenserPtr := dispenserPtr^.Next;
InitActor(rockPtr^, AROCK, dispenserPtr^.xy);
rockPtr := rockPtr^.Next;
END;
m.AnyRocksPending := TRUE;
END;


96
LadderTP/LADTYPE.PAS Normal file
View File

@ -0,0 +1,96 @@
TYPE
Str80Type = STRING[80];
ScoreType = (ScoreReset, ScoreRock, ScoreStatue, ScoreMoney);
NameStringType = STRING[20];
LayoutType = ARRAY[1..LevelRows] OF ARRAY[1..LevelCols] OF CHAR;
LevelType = RECORD
Name : NameStringType;
InitialBonusTime : INTEGER;
Rocks : INTEGER;
Layout : LayoutType;
END;
{ The Action constants define what the Actor currently is, }
{ or is requested to be, doing }
ActionType = (
PENDING,
NONE,
STOPPED,
UP,
UPRIGHT,
RIGHT,
DOWNRIGHT,
DOWN,
DOWNLEFT,
LEFT,
UPLEFT,
FALLING,
JUMP, { Generic jump set by keyhandler }
JUMPRIGHT,
JUMPUP,
JUMPLEFT,
ACTIONEND
);
ActionArrayType = ARRAY[1..JumpsLen] OF ActionType;
KindType = ( ALAD, AROCK );
RockPointerType = ^ActorType;
{ The Actor struct holds info about an actor ie, the Lad or a Rock }
ActorType = RECORD
AType : KindType;
X,Y : INTEGER;
Ch : CHAR;
Dir, DirRequest : ActionType;
JumpStep : INTEGER;
Next : RockPointerType;
END;
{ The MapData hold all info about a map }
MapDataType = RECORD
Name : NameStringType;
Field : LayoutType;
LadsRemaining : INTEGER;
NumRocks : INTEGER;
Rocks : RockPointerType;
AnyRocksPending : BOOLEAN;
Level : INTEGER;
Score : INTEGER;
InitialBonusTime : INTEGER;
RemainingBonusTime : INTEGER;
ScoreText : Str80type;
END;
XYtype = RECORD
x, y : INTEGER;
END;
DispenserPointerType = ^DispenserType;
DispenserType = RECORD
xy : XYtype;
next : DispenserPointerType;
END;
{ layout of LADDER.DAT data file }
DataFileType = RECORD
TerminalName : STRING[DataFileStrLength];
MoveCsrPrefix : STRING[DataFileStrLength];
MoveCsrSeparator : STRING[DataFileStrLength];
MoveCsrSuffix : STRING[DataFileStrLength];
UnkEscSeq : STRING[DataFileStrLength]; { not sure what this is... initialization? }
ClrScrStr : STRING[DataFileStrLength];
Flags : ARRAY[0..DataFileStrLength] OF CHAR;
Keys : ARRAY[0..DataFileStrLength] OF CHAR;
Highs : ARRAY[1..NumHighScores] OF ARRAY[0..DataFileStrLength] OF BYTE;
Unused1 : ARRAY[0..DataFileStrLength] OF BYTE;
Unused2 : ARRAY[0..DataFileStrLength] OF BYTE; { padding to next 128 bytes }
Unused3 : ARRAY[0..DataFileStrLength] OF BYTE;
END;
HighScoreType = RECORD
Score : INTEGER;
Name : STRING[DataFileNameLength];
END;


24
LadderTP/LADUTILS.PAS Normal file
View File

@ -0,0 +1,24 @@
{
Some terminal routines not handled by Turbo Pascal.
CursOff: turn the cursor off (set the string in LADCONST.PAS)
CursOn: turn the cursor on (set the string in LADCONST.PAS)
Beep: ring the terminal bell
}
PROCEDURE CursOff;
BEGIN
Write(CursOffStr);
END;
PROCEDURE CursOn;
BEGIN
Write(CursOnStr);
END;
PROCEDURE Beep;
BEGIN
IF sound THEN
Write(#7);
END;


17
LadderTP/LADVAR.PAS Normal file
View File

@ -0,0 +1,17 @@
VAR
dispensers : DispenserPointerType; { rock dispensers linked list }
numDispensers : INTEGER; { # of dispensers on current level }
lastScore : INTEGER; { score from last completed game }
playSpeed : INTEGER;
m : MapDataType;
lad : ActorType;
ladXY : XYtype; { starting position of lad }
nextNewLad : INTEGER; { score for next new lad awarded }
dataFileContents : DataFileType;
highScores : ARRAY[1..NumHighScores] OF HighScoreType;
sound : BOOLEAN; { TRUE for sound }
insults : BOOLEAN; { TRUE for insults }
levelCycle : INTEGER;
displayLevel : INTEGER; { displayed map level }
upKey, downKey, leftKey, rightKey : CHAR; { lad direction control keys }


108
LadderTP/README.md Normal file
View File

@ -0,0 +1,108 @@
# Ladder
The classic CP/M game Ladder reverse engineered in Turbo Pascal.
Original GitHub repository: https://github.com/mecparts/Ladder
Additional information and compiled version by Anna Christina Naß <acn@acn.wtf>
I've added a compiled ``LADDER.COM`` file for just playing the game (on a VT100
terminal at about 8 MHz, eg. a RC2014), and added more information on
compiling the game.
## Keys
The file ``LADDER.DAT`` is the configuration file that holds the key definitions.
These can be changed by using ``LADCONF.COM`` (the terminal definition
in LADCONF is not used! This LADDER uses the Turbo Pascal terminal definitions).
The default keys in this ``LADDER.DAT`` are:
Up = 8 Down = 2 Left = 4 Right = 6
Jump = Space Stop = Other
(Jump and Stop are not configurable)
Also, "bell" sound is enabled and "wise crack marks" are disabled.
## Compiling notes from Anna:
To compile LADDER, just use Turbo Pascal from the ``TP/`` directory.
It's TP 3.0A for CP/M-80, only without the demo source code.
* Copy the TP files together with the LADDER files to one disk
* The terminal definition is already set to ANSI and the speed is set to 8 MHz (for a
standard RC2014), so running TINST is not needed
* Run Turbo Pascal using TURBO
* Set the compiler options to "compile -> Com-file" (press O, C, Q)
* Compile ``LADDER.PAS``: press C (Compile), then enter ``ladder`` as Work file name.
This should result in a ``LADDER.COM`` file.
The rest of this file is mostly the README from the original repository.
# Original README content:
## About
This is a rewrite in Turbo Pascal of the classic CP/M game "Ladder",
originally written by Yahoo Software (not Yahoo!).
Ladder is an ASCII character based platform arcade game similar to
Donkey Kong. You travel through levels with platforms and ladders
where rocks fall down from the top while you collect statues
before reaching the exit.
Back in 1999 Stephen Ostermiller made a version of [Ladder in
Java](http://ostermiller.org/ladder/). Later, Mats Engstrom of
SmallRoomLabs started another version in of [Ladder in
golang](https://github.com/SmallRoomLabs/ladder). Between my own
memories of playing the original game on a Kaypro, and Stephen
Ostermiller's and Mats Engstrom's code, I was able to come up
with this version.
This version will use the original LADCONF.COM configuration program
and LADDER.DAT configuration file. Since this version is a Turbo
Pascal program, the terminal configuration portion of LADCONF
isn't used, though it still does set up the movement keys, sound
and back chatter options.
## Compiling the game
You'd need Turbo Pascal, of course. You'll also need to edit
LADCONST.PAS to set the cursor on and off sequences for your
terminal. LADDER.PAS is the main part of the program. I've
successfully compiled this on a 58K CP/M system, so available RAM
isn't a particularly critical limitation.
Once you've compiled LADDER.COM, copy LADCONF.COM to the same user area.
If you don't have a LADDER.DAT file, when you run LADDER the first time
it'll automatically load LADCONF to set up the movement keys and
options, then transfer you back to LADDER.
## Limitations
At the moment, once you've successfully completed the 7th distinct level
(Gang Land), the program just cycles through all 7 seven levels over and
over again until the bonus time becomes too short to actually finish a
level. If anyone knows what the original program actually did (I never
managed to get anywhere near to completing the original game), let me
know and I'll see what I can do.
The Delay(ms) call in Turbo Pascal only works for a Z80 running
at up to 32MHz (and TINST will only allow you to specify a value of up
to 20MHZ if I recall correctly). So if you're trying to run this on a
system with an effective clock speed of greater than 32MHz, you're going
to have to come up with another mechanism. That's not an insurmountable
roadblock though; on my 144MHz-Z80-equivalent RunCPM box running on a
Teensy 4.0, I patched the Turbo Pascal runtime to make a call to a BDOS
extension I created to call the Arduino's delay() function. Works like
a charm. If your system includes any kind of millisecond counter you can
read, that's a good spot to start looking.
## References
[Original Ladder game](http://www.classiccmp.org/cpmarchives/cpm/Software/WalnutCD/lambda/soundpot/f/ladder13.lbr)<br>
[Ladder in Java](http://ostermiller.org/ladder/)<br>
[Ladder in golang](https://github.com/SmallRoomLabs/ladder)<br>

181
LadderTP/TP/READ.ME Normal file
View File

@ -0,0 +1,181 @@
Welcome to TURBO PASCAL Version 3.0!
------------------------------------
In spite of all efforts, some errors have found their way into
the new TURBO 3.0 manual. This file contains all the necessary
corrections and additions, and we apologize for any inconvenience
this may cause you.
Please make a working copy of your TURBO disk and store the ori-
ginal in a safe place. For help making a backup copy, please
refer to appendix M of the TURBO PASCAL Reference Manual.
NOTEº  Youò  TURBÏ  PASCAÌ disë haó beeî pre-installeä  foò  youò <20>
    Microbeå disë system¬ paù nï attentioî tï thå manuaì witè regardó <20>
    to installing TURBO PASCAL.
*******************************************
* *
* Need help with TURBO? Please see *
* Appendix N in your Reference Manual *
* for answers to common questions. *
* *
*******************************************
-------------------
Contents of the READ.ME File
----------------------------
1. CORRECTIONS to the 3.0 Reference Manual [ All versions ]
2. OMMISSIONS from the 3.0 Reference Manual [ All versions ]
3. New FEATURES [ CP/M-80 ]
4. ADDITIONAL FILE LIST [ CP/M-80 ]
-------------------
CORRECTIONS
-----------
Page 253 - MOV AL,[BP-1]
------------------------
The correct statement is: MOV AL,[BP+4]
Page 293 - TURBO-BCD will compile and run any program
-----------------------------------------------------
Well - almost. The Real functions Sin, Cos, ArcTan, Ln, Exp,
and Sqrt and the pre-declared constant Pi are not available
in TURBOBCD. Š
-------------------
OMISSIONS
---------
User Written Error Handlers
---------------------------
In Turbo Pascal 3.0 you may write your own error handler,
which is called in case of an I/O or Run-time error. The
procedure must have the following header:
procedure Error(ErrNo, ErrAddr: Integer);
The name of the procedure and its parameters are unim-
portant, as long as it is a procedure with two value
parameters of type Integer.
The value passed in ErrNo is the error type and number. The
most significant byte, i.e. "Hi(ErrNo)", contains the error
type, and the least significant byte, i.e. "Lo(ErrNo)",
contains the error number (see Appendix F or G in the Turbo
Pascal Manual).
The following error types are defined:
0 User Break (Ctrl-C).
1 I/O error.
2 Run-time error.
In case of a user interrupt (Ctrl-C), the low byte of
"ErrNo" is always 1. "ErrAddr" contains the address (offset
in Code Segment for 16 bit versions) of the error.
To activate the error handler, assign its offset address
to the standard variable "ErrorPtr", i.e.
ErrorPtr:=Ofs(Error); { 16 bit } or
ErrorPtr:=Addr(Error); { 8 bit }
There are no limits to what an error handler may do. Typi-
cally it will close all open files, output an error mes-
sage, and call the Halt standard procedure to terminate the
program. If an error handler returns, i.e. if it does
not call Halt, or if an error occurs within an error
handler, Turbo Pascal will itself output the error message
and terminate the program.
------------------- Š
NEW FEATURES OF CP/M-80 IMPLEMENTATION OF
TURBO 3.0
- AN OVERVIEW -
-----------------------------------------
Inline
------
A constant identifier used in an INLINE statement does not
always generate two bytes of code.
Files
-----
New FIB formats.
Optional 4th parameter on Blockread/Write returns number of
blocks actually read.
SeekEoln function.
SeekEof function.
Misc.
-----
Exit procedure - To exit the current block
OvrDrive procedure - To specify the drive on which to find overlays
ParamCount function - Gives number of characters in the command buffer
ParamStr function - Gives the string of characters in the command line
Overlays
--------
Overlay files are opened and closed every time they are
accessed. Therefore, there is never a need to specifically
close an overlay file.
The Y compiler directive is no longer supported. Instead,
the OvrPath (MS-DOS) or OvrDrive (CP/M) standard proce-
dures may be used to specify the drive and subdirectory
in which overlay files reside.
Please note that run-time error F0 indicates that your over-
lay file is missing or is called recursively. (This error
number is omitted from the Reference Manual but is included
elsewhere in this file.)
-------------------
TURBO PASCAL Version 3.0
CP/M-80
Additional File List
In addition to the list of files mentioned in Chapter 1 of
your TURBO Reference Manual, the following files are included
on your TURBO disk: Š
Sample programs
---------------
LISTER PAS - simple program to list your Pascal source
CMDLIN PAS - get parameters from the command line
MC PAS - sample spreadsheet program - MAIN MODULE
MC-MOD00 INC - sample spreadsheet program - INCLUDE MODULE 00
MC-MOD01 INC - sample spreadsheet program - INCLUDE MODULE 01
MC-MOD02 INC - sample spreadsheet program - INCLUDE MODULE 02
MC-MOD03 INC - sample spreadsheet program - INCLUDE MODULE 03
MC-MOD04 INC - sample spreadsheet program - INCLUDE MODULE 04
MC-MOD05 INC - sample spreadsheet program - INCLUDE MODULE 05
MC HLP - spreadsheet help file
MCDEMO MCS - spreadsheet data file (not for use with TURBO-87)
---------------------------------------------------------------------


BIN
LadderTP/TP/TINST.COM Normal file

Binary file not shown.

BIN
LadderTP/TP/TINST.DTA Normal file

Binary file not shown.

123
LadderTP/TP/TINST.MSG Normal file
View File

@ -0,0 +1,123 @@
1 TURBO Pascal installation menu.
2 Choose installation item from the following:
3
4 [S]creen installation | [C]ommand installation | [Q]uit
5
6 Enter S, C, or Q:
10 Duplicate definition. Error occurred between question
11 Commands starting with the same letter must have the same length.
Error occurred between question
12 The total maximum length of commands are execeeded
13 ->
14 CURSOR MOVEMENTS:
20 Character left
21 Alternative
22 Character right
23 Word left
24 Word right
25 Line up
26 Line down
27 Scroll down
28 Scroll up
29 Page up
30 Page down
31 To left on line
32 To right on line
33 To top of page
34 To bottom of page
35 To top of file
36 To end of file
37 To begining of block
38 To end of block
39 To last cursor position
15 INSERT & DELETE:
40 Insert mode on/off
41 Insert line
42 Delete line
43 Delete to end of line
44 Delete right word
45 Delete character under cursor
46 Delete left character
47 Alternative
16 BLOCK COMMANDS:
48 Mark block begin
49 Mark block end
50 Mark single word
51 Hide/display block
52 Copy block
53 Move block
54 Delete block
55 Read block from disk
56 Write block to disk
17 MISC. EDITING COMMANDS:
57 End edit
58 Tab
59 Auto tab on/off
60 Restore line
61 Find
62 Find & replace
63 Repeat last find
64 Control character prefix
101 Nothing
^Q: Quit, ^R: Last page, ^C: Next page, <RETURN>: Select terminal:
Wait Sorting Definitions
Change to:
(Y/N)?
y
n
Text file name:
Command:
Numeric entry expected
Legal range is
, please re-enter:
Choose one of the following terminals:
None of the above ( Max. 20 Characters )
Delete a definition ( Max. 20 Characters )
Which terminal? (Enter no. or ^Q to exit):
Delete terminal? (Enter no. or ^Q to exit):
Do you want to modify this definition before installation?
Terminal type:
Send an initialization string to the terminal?
Initializaion defined as a command string? (No = a file)
Send a reset string to the terminal
Reset defined as a command? (No = a file)
CURSOR LEAD-IN command:
CURSOR POSITIONING COMMAND to send between line and column:
CURSOR POSITIONING COMMAND to send after both line and column:
Column first
OFFSET to add to LINE:
OFFSET to add to COLUMN:
Binary address
Number of ASCII digits (2 or 3):
CLEAR SCREEN command:
Does CLEAR SCREEN also HOME cursor
HOME command:
DELETE LINE command:
INSERT LINE command:
ERASE TO END OF LINE command:
START HIGHLIGHTING command:
END HIGHLIGHTING command:
Number of rows (lines) on your screen:
Number of columns on your screen:
Delay after CURSOR ADDRESS (0-255 ms):
Delay after CLEAR, DELETE and INSERT (0-255 ms):
Delay after ERASE TO END OF LINE and HIGHLIGHT (0-255 ms):
Is this definition correct?
Hardware dependent information
Operating frequency of your microprocessor in MHz (for delays):
pendent information
Operating frequency of your microprocessor in MHz (for delays):


BIN
LadderTP/TP/TURBO.COM Normal file

Binary file not shown.

101
LadderTP/TP/TURBO.MSG Normal file
View File

@ -0,0 +1,101 @@
 are not allowed
 can not be
 constant
 does not
 expression
 identifier
 file
 here
Integer
File
Illegal
 or
Undefined
 match
 real
String
Textfile
 out of range
 variable
 overflow
 expected
 type
Invalid
 pointer
01';'
02':'
03','
04'('
05')'
06'='
07':='
08'['
09']'
10'.'
11'..'
12BEGIN
13DO
14END
15OF
17THEN
18TO DOWNTO
20Boolean
21 
22 
23 
24 
25 
26 
27 
28Pointer
29Record
30Simple
31Simple
32
33
34
35
36Type
37Untyped
40 label
41Unknown syntax error
42 in preceding definitions
43Duplicate label
44Type mismatch
45
46 and CASE selector
47Operand(s) operator
48 result
49  length
50 length
51 subrange base
52Lower bound > upper bound
53Reserved word
54 assignment
55 exceeds line
56Error in integer
57Error in
58 character in
60s
61 s ands
62Structureds
63s
64s and untypeds
65Untypeds
66I/O
67 s must be parameters
68 componentss
69dering of fields
70Set base
71 GOTO
72Label not within current block
73 FORWARD procedure(s)
74INLINE error
75 use of ABSOLUTE
90 not found
91Unexpected end of source
97Too many nested WITH's
98Memory
99Compilerd WITH's
98Memory
99Compiler

BIN
LadderTP/TP/TURBO.OVR Normal file

Binary file not shown.

View File

@ -18,6 +18,7 @@ See this repository: https://git.imzadi.de/acn/backgammon-vt100
* [2048](2048/)
* [Ladder](Ladder/)
* [Ladder (Turbo Pascal version)](LadderTP/)
* [Rogue](Rogue/)
* [Wanderer](Wanderer/)
* [CatChum](CatChum/)
@ -34,7 +35,6 @@ See this repository: https://git.imzadi.de/acn/backgammon-vt100
* [Gorilla.bas](https://github.com/sblendorio/gorilla-cpm): GORILLA.BAS port to CP/M in Turbo Modula-2. Supported terminals: VT52, VT100, ANSI, ADM-31, KayPro, C128, Memotech monochrome, CPC / Zenith Z19
* [Hangman](https://github.com/sblendorio/hangman-cpm): C implementation for CP/M of the classic "hangman" included in "bsdgames" popular package for UNIX
* [Ladder](https://github.com/mecparts/Ladder): Turbo Pascal reimplementation of Ladder (see above)
## HD image