home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
DP Tool Club 16
/
CD_ASCQ_16_0994.iso
/
news
/
573
/
3dlab101
/
l3dworld.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
1994-05-26
|
20KB
|
438 lines
{────────────────────────────────────────────────────────────────────────────}
{───( C ) Copyright 1994 By Kimmo Fredriksson.───────────────────────────────}
{────────────────────────────────────────────────────────────────────────────}
{───You may use this unit freely in your programs, and distribute them,──────}
{───but you are *NOT* allowed to distribute any modified form of this────────}
{───unit, not source, nor the compiled TPU, TPP or whatsoever, *without*─────}
{───my permission! In it's original form, this source is freeware.───────────}
{────────────────────────────────────────────────────────────────────────────}
{───Internet email: Kimmo.Fredriksson@Helsinki.FI────────────────────────────}
{────────────────────────────────────────────────────────────────────────────}
{────────────────────────────────────────────────────────────────────────────}
{───If you want the Turbo Pascal and assembler source code for the TxtMap────}
{───Unit, register today. Send $20 (or 100 Fmk) to me, and I'll send all─────}
{───the source to you.───────────────────────────────────────────────────────}
{────────────────────────────────────────────────────────────────────────────}
{─────────Kimmo Fredriksson──────────────────────────────────────────────────}
{─────────Silvontie 38───────────────────────────────────────────────────────}
{─────────37740 Haukila──────────────────────────────────────────────────────}
{─────────FINLAND────────────────────────────────────────────────────────────}
{────────────────────────────────────────────────────────────────────────────}
{$A+,B-,D-,E-,F-,G+,I-,L-,N-,O-,P+,Q-,R-,S-,T-,V-,X+}
UNIT L3DWorld;
INTERFACE
USES TxtMap;
CONST Copyright = '(C) 1994 By Kimmo Fredriksson';
ChkHit : Boolean = TRUE; { check hit-the-wall ? }
HitDist = 4 * WorldXZ DIV 3; { hits the wall at this dist. }
CONST WXDim = 256 DIV 2; { Size of the world }
WZDim = 256 DIV 2;
MinWX : Word = WXDim; { These are updated when }
MinWZ : Word = WZDim; { building the world so, that }
MaxWX : Word = 0; { MinW? and MaxW? tell the }
MaxWZ : Word = 0; { upper-left and lower-right }
{ corners of world }
TYPE IntARRAY = ARRAY[ 0..16383 ] OF Integer; { used to casts }
ByteARRAY = ARRAY[ 0..16383 ] OF Byte;
IntAPtr = ^IntARRAY;
ByteAPtr = ^ByteARRAY;
WallTableTYPE = ARRAY[ 0..WXDim * 2 DIV 8 - 1, 0..WZDim * 2 - 1 ] OF Byte;
{ This data structure is used to hit-the-wall-check. }
{ If the bit corresponding to players position is set, then }
{ player has hit the wall }
VAR WallTable : ^WallTableTYPE;
PROCEDURE SetWallTxtObjX( xF, xT, Zc : Integer; TI : Byte );
PROCEDURE SetWallTxtObjZ( zF, zT, Xc : Integer; TI : Byte );
PROCEDURE SetWallTxtObjBoxIn ( xF, zF, xT, zT : Integer; T0, T1, T2, T3 : Byte );
PROCEDURE SetWallTxtObjBoxOut( xF, zF, xT, zT : Integer; T0, T1, T2, T3 : Byte );
PROCEDURE SetWallTxtObjPoly( xc, zc : IntAPtr; TI : ByteAPtr; n : Word );
FUNCTION GetOneWall( xc, zc : Integer ) : Boolean;
PROCEDURE MovePoint( VAR XP, ZP : LongInt; VAR X, Z : Integer; Xd, Zd, Angle : Integer );
PROCEDURE MoveEye( Angle, Speed : Integer );
PROCEDURE TurnEye( Angle : Integer );
PROCEDURE SetEyePos( EyeX, EyeZ, Angle : Integer );
IMPLEMENTATION
USES AsmSys;
{
╔═════════════════════════════════════════════════════════════════════════╗
║ PROCEDURE Initilize ║
╚═════════════════════════════════════════════════════════════════════════╝
}
PROCEDURE Initialize;
BEGIN
NormX := WXDim; { needed to convert user-points to porgrams own internal }
NormZ := WZDim; { form }
New( WallTable );
FillChar( WallTable^, SizeOf( WallTableTYPE ), 0 ); { begin with no walls }
END;
{
╔═════════════════════════════════════════════════════════════════════════╗
║ PROCEDURE SetOneWall ║
╟─────────────────────────────────────────────────────────────────────────╢
║ Input : Coordinates of the wall ║
╟─────────────────────────────────────────────────────────────────────────╢
║ Set the bit on int the WallTable, corresponding the input wall ║
╚═════════════════════════════════════════════════════════════════════════╝
}
PROCEDURE SetOneWall( xc, zc : Integer );
VAR msk : Byte;
BEGIN
MinWX := MinIn( MinWX, xc ); { update the upper-left and lower-right }
MinWZ := MinIn( MinWZ, zc ); { coordinates of the world, }
MaxWX := MaxIn( MaxWX, xc ); { needed to draw the map }
MaxWZ := MaxIn( MaxWZ, zc );
msk := 1 SHL ( Abs( xc MOD 8 )); { 8-bits/byte --> 8 walls/byte }
xc := xc DIV 8;
WallTable^[ xc, zc ] := WallTable^[ xc, zc ] OR msk;
END;
{
╔═════════════════════════════════════════════════════════════════════════╗
║ FUNCTION GetOneWall ║
╟─────────────────────────────────────────────────────────────────────────╢
║ Input : coordinates to check for wall ║
║ Output : TRUE, if wall found, FALSE otherwise ║
╟─────────────────────────────────────────────────────────────────────────╢
║ This function is needed to check if player walked against the wall. ║
║ Look also SetOneWall. ║
╚═════════════════════════════════════════════════════════════════════════╝
}
FUNCTION GetOneWall( xc, zc : Integer ) : Boolean;
VAR msk : Byte;
BEGIN
msk := 1 SHL ( Abs( xc MOD 8 ));
xc := xc DIV 8;
GetOneWall := ( WallTable^[ xc, zc ] AND msk ) <> 0;
END;
{
╔═════════════════════════════════════════════════════════════════════════╗
║ FUNCTION HitTheWallX ║
╟─────────────────────────────────────────────────────────────────────────╢
║ Input : moving point's x, z coordinates, and angle of direction ║
║ Output : TRUE, if point hit the wall in x-direction, FALSE otherwise ║
╟─────────────────────────────────────────────────────────────────────────╢
║ xc, zc are 8-bit fixed-point numbers. ║
║ HitTheWallZ doas the same as this, but in Z-direction ║
╚═════════════════════════════════════════════════════════════════════════╝
}
FUNCTION HitTheWallX( xc, zc : LongInt; Angle : Integer ) : Boolean;
VAR z0, z1 : Integer;
BEGIN
xc := xc * 2; { walls was saved that way... }
zc := zc * 2;
IF ChkHit THEN { do we care the hits of... }
BEGIN
z0 := ( zc DIV 256 ) DIV WorldXZ; { position in WallTable }
z1 := ( zc DIV 256 + WorldXZ DIV 2 ) DIV WorldXZ;
xc := xc DIV 256;
zc := zc DIV 256 DIV WorldXZ;
IF ( Angle >= 0 ) AND ( Angle < 180 ) THEN
xc := ( xc + WorldXZ + HitDist ) DIV WorldXZ
ELSE
xc := ( xc - HitDist ) DIV WorldXZ;
HitTheWallX := GetOneWall( xc, zc ) OR GetOneWall( xc, zc + 1 )
END
ELSE
HitTheWallX := FALSE
END;
{
╔═════════════════════════════════════════════════════════════════════════╗
║ FUNCTION HitTheWallZ ║
╟─────────────────────────────────────────────────────────────────────────╢
║ Input : moving point's x, z coordinates, and angle of direction ║
║ Output : TRUE, if point hit the wall, FALSE otherwise ║
╟─────────────────────────────────────────────────────────────────────────╢
║ xc, zc are 8-bit fixed-point numbers. ║
║ HitTheWallX does the same as this, but in the X-direction. ║
╚═════════════════════════════════════════════════════════════════════════╝
}
FUNCTION HitTheWallZ( xc, zc : LongInt; Angle : Integer ) : Boolean;
VAR x0, x1 : Integer;
BEGIN
xc := xc * 2;
zc := zc * 2;
IF ChkHit THEN
BEGIN
x0 := ( xc DIV 256 ) DIV WorldXZ;
x1 := ( xc DIV 256 + WorldXZ DIV 2 ) DIV WorldXZ;
xc := xc DIV 256 DIV WorldXZ;
zc := zc DIV 256;
IF ( Angle >= 90 ) AND ( Angle < 270 ) THEN
zc := ( zc - HitDist ) DIV WorldXZ
ELSE
zc := ( zc + WorldXZ + HitDist ) DIV WorldXZ;
HitTheWallZ := GetOneWall( xc, zc ) OR GetOneWall( xc + 1, zc )
END
ELSE
HitTheWallZ := FALSE
END;
{
╔═════════════════════════════════════════════════════════════════════════╗
║ PROCEDURE MakeWall ║
╟─────────────────────────────────────────────────────────────────────────╢
║ Input : Wall-tile's Left and Right coordinates, when viewing from Front║
╟─────────────────────────────────────────────────────────────────────────╢
║ Set the corresponding bits on in the WallTable, which we check out for ║
║ any walls after each step. ║
╚═════════════════════════════════════════════════════════════════════════╝
}
PROCEDURE MakeWall( x1, x2, z1, z2 : Integer );
BEGIN
x1 := ( x1 + WXDim DIV 2 ) * 2;
x2 := ( x2 + WXDim DIV 2 ) * 2;
z1 := ( z1 + WZDim DIV 2 ) * 2;
z2 := ( z2 + WZDim DIV 2 ) * 2;
SetOneWall( x1, z1 ); { left edge }
SetOneWall(( x1 + x2 ) DIV 2, ( z1 + z2 ) DIV 2 ); { center }
SetOneWall( x2, z2 ); { right edge }
IF x1 = x2 THEN { where the slab faces to ? }
BEGIN
Inc( x1, Sgn( z1 - z2 ));
Inc( x2, Sgn( z1 - z2 ));
END
ELSE
BEGIN
Inc( z1, Sgn( x2 - x1 ));
Inc( z2, Sgn( x2 - x1 ));
END;
SetOneWall( x1, z1 ); { make double wall }
SetOneWall(( x1 + x2 ) DIV 2, ( z1 + z2 ) DIV 2 );
SetOneWall( x2, z2 );
END;
{
╔═════════════════════════════════════════════════════════════════════════╗
║ PROCEDURE InitWall ║
╟─────────────────────────────────────────────────────────────────────────╢
║ Input : Wall-tile's Left and Right coordinates, when viewing from ║
║ Front, and index of desired texture ║
╚═════════════════════════════════════════════════════════════════════════╝
}
PROCEDURE InitWall( x1, x2, z1, z2 : Integer; TI : Byte );
BEGIN
MakeWall( x1, x2, z1, z2 );
InitWallTxtObj( x1, x2, z1, z2, TI );
END;
{
╔═════════════════════════════════════════════════════════════════════════╗
║ PROCEDURE SetWallTxtObjX ║
╟─────────────────────────────────────────────────────────────────────────╢
║ Input : Left and Right X-coordinates of the wall, when viewing from ║
║ Front side, and corresponding Z-coordinate and texture inexes. ║
╟─────────────────────────────────────────────────────────────────────────╢
║ Make a wall which consists of texture TI. Coordinates must be given in ║
║ right order (first left, then right, viewed from front), because if ║
║ vewed from wrong side, the wall is invisible. ║
║ See also the SetWallTxtObjZ-procedure. ║
╚═════════════════════════════════════════════════════════════════════════╝
}
PROCEDURE SetWallTxtObjX( xF, xT, Zc : Integer; TI : Byte );
VAR i : Integer;
BEGIN
IF xF = xT THEN Exit;
IF xF < xT THEN
FOR i := xF TO Pred( xT ) DO InitWall( i, i + 1, Zc, Zc, TI )
ELSE
FOR i := xF DOWNTO Succ( xT ) DO InitWall( i, i - 1, Zc, Zc, TI );
InitLongWall( xF, xT, Zc, Zc );
END;
{
╔═════════════════════════════════════════════════════════════════════════╗
║ PROCEDURE SetWallTxtObjZ ║
╟─────────────────────────────────────────────────────────────────────────╢
║ Input : Left and Right Z-coordinates of the wall, when viewing from ║
║ Front side, and corresponding X-coordinate and texture inexes. ║
╟─────────────────────────────────────────────────────────────────────────╢
║ Make a wall which consists of texture TI. Coordinates must be given in ║
║ right order (first left, then right, viewed from front), because if ║
║ vewed from wrong side, the wall is invisible. ║
║ See also the SetWallTxtObjX-procedure. ║
╚═════════════════════════════════════════════════════════════════════════╝
}
PROCEDURE SetWallTxtObjZ( zF, zT, Xc : Integer; TI : Byte );
VAR i : Integer;
BEGIN
IF zF = zT THEN Exit;
IF zF < zT THEN
FOR i := zF TO Pred( zT ) DO InitWall( Xc, Xc, i, i + 1, TI )
ELSE
FOR i := zF DOWNTO Succ( zT ) DO InitWall( Xc, Xc, i, i - 1, TI );
InitLongWall( Xc, Xc, zF, zT );
END;
{
╔═════════════════════════════════════════════════════════════════════════╗
║ PROCEDURE SetWallTxtObjBoxIn ║
╟─────────────────────────────────────────────────────────────────────────╢
║ Input : Coordinates of opposite corners, and texture indexes ║
║ corresponding to each side ║
╟─────────────────────────────────────────────────────────────────────────╢
║ Make a box using walls. Viewer must be inside of this box, because it ║
║ will not be visible when viewed outside. ║
╚═════════════════════════════════════════════════════════════════════════╝
}
PROCEDURE SetWallTxtObjBoxIn( xF, zF, xT, zT : Integer; T0, T1, T2, T3 : Byte );
BEGIN
IF ( zF = zT ) OR ( xF = xT ) THEN Exit;
IF xF > xT THEN SwapInt( xF, xT );
IF zF < zT THEN SwapInt( zF, zT );
SetWallTxtObjX( xF, xT, zF, T0 );
SetWallTxtObjZ( zF, zT, xT, T1 );
SetWallTxtObjX( xT, xF, zT, T2 );
SetWallTxtObjZ( zT, zF, xF, T3 );
END;
{
╔═════════════════════════════════════════════════════════════════════════╗
║ PROCEDURE SetWallTxtObjBoxOut ║
╟─────────────────────────────────────────────────────────────────────────╢
║ Input : Coordinates of opposite corners, and texture indexes ║
║ corresponding to each side ║
╟─────────────────────────────────────────────────────────────────────────╢
║ Make a box using walls. Viewer must be outside of this box, because it ║
║ will not be visible when viewed inside. ║
╚═════════════════════════════════════════════════════════════════════════╝
}
PROCEDURE SetWallTxtObjBoxOut( xF, zF, xT, zT : Integer; T0, T1, T2, T3 : Byte );
BEGIN
IF ( zF = zT ) OR ( xF = xT ) THEN Exit;
IF xF > xT THEN SwapInt( xF, xT );
IF zF < zT THEN SwapInt( zF, zT );
SetWallTxtObjX( xT, xF, zF, T0 );
SetWallTxtObjZ( zT, zF, xT, T1 );
SetWallTxtObjX( xF, xT, zT, T2 );
SetWallTxtObjZ( zF, zT, xF, T3 );
END;
{
╔═════════════════════════════════════════════════════════════════════════╗
║ PROCEDURE SetWallTxtObjPoly ║
╟─────────────────────────────────────────────────────────────────────────╢
║ Input : Pointers to tables, that contai the x, z coordinates, and ║
║ corresponding texture indexes, and number of the points. ║
╟─────────────────────────────────────────────────────────────────────────╢
║ Make n walls, which starting and ending points are found in the tables. ║
║ You MUST specify the walls so, that first comes the Left edge, and then ║
║ the Right edge, when viewing from Front. ║
╚═════════════════════════════════════════════════════════════════════════╝
}
PROCEDURE SetWallTxtObjPoly( xc, zc : IntAPtr; TI : ByteAPtr; n : Word );
VAR i, j : Integer;
BEGIN
FOR i := 0 TO n - 1 DO
BEGIN
j := ( i + 1 ) MOD n; { make closed wall }
IF xc^[ i ] = xc^[ j ] THEN { where does it face to ? }
SetWallTxtObjZ( zc^[ i ], zc^[ j ], xc^[ i ], TI^[ i ] )
ELSE
SetWallTxtObjX( xc^[ i ], xc^[ j ], zc^[ i ], TI^[ i ] );
END
END;
{
╔═════════════════════════════════════════════════════════════════════════╗
║ PROCEDURE MovePoint ║
╟─────────────────────────────────────────────────────────────────────────╢
║ Input : XP, ZP are 8-bit fixed-pooint coordinates; X, Z are their inte-║
║ ger parts; Xd, Zd, movement in X and Z directions; Angle is ║
║ viewing angle, not necessarely to moving direction ║
╟─────────────────────────────────────────────────────────────────────────╢
║ Move the point, and if it didn't hit the wall, return new position. ║
╚═════════════════════════════════════════════════════════════════════════╝
}
PROCEDURE MovePoint( VAR XP, ZP : LongInt; VAR X, Z : Integer; Xd, Zd, Angle : Integer );
VAR Xtmp0, Ztmp0, Xtmp1, Ztmp1 : LongInt;
BEGIN
Xtmp0 := XP + Xd;
Ztmp0 := ZP + Zd; { check X and Z directions separately }
IF NOT HitTheWallX( Xtmp0, ZP, Angle ) THEN Xtmp1 := Xtmp0 ELSE Xtmp1 := XP;
IF NOT HitTheWallZ( XP, Ztmp0, Angle ) THEN Ztmp1 := Ztmp0 ELSE Ztmp1 := ZP;
XP := Xtmp1; { if we didn't hit the wall, return new coordinates }
ZP := Ztmp1;
X := XP DIV 256;
Z := ZP DIV 256
END;
{
╔═════════════════════════════════════════════════════════════════════════╗
║ PROCEDURE MoveEye ║
╟─────────────────────────────────────────────────────────────────────────╢
║ Input : Speed and direction of the movement ║
╟─────────────────────────────────────────────────────────────────────────╢
║ If you want to move backwards, for examble, you should use Angle of 180 ║
║ degrees, and positive speed, and NOT angle of 0, and negative speed, ║
║ because that would corrupt the hit-the-wall checking. ║
╚═════════════════════════════════════════════════════════════════════════╝
}
PROCEDURE MoveEye( Angle, Speed : Integer );
VAR Xd, Zd : Integer;
BEGIN
WITH EyePA DO
BEGIN
Angle := ( YAng + Angle ) MOD 360; { make sure that the angle }
IF Angle < 0 THEN Inc( Angle, 360 ); { is between 0..359 }
Xd := Integer( DSin[ Angle ] ) * Speed;
Zd := Integer( DCos[ Angle ] ) * Speed;
MovePoint( XP, ZP, X, Z, Xd, Zd, Angle )
END
END;
{
╔═════════════════════════════════════════════════════════════════════════╗
║ PROCEDURE TurnEye ║
╟─────────────────────────────────────────────────────────────────────────╢
║ Input : how many degrees to turn, and turning direction (sign) ║
╟─────────────────────────────────────────────────────────────────────────╢
║ Angle is converted to between 0-359 ║
╚═════════════════════════════════════════════════════════════════════════╝
}
PROCEDURE TurnEye( Angle : Integer );
BEGIN
WITH EyePA DO
BEGIN
YAng := ( YAng + Angle ) MOD 360;
IF YAng < 0 THEN Inc( YAng, 360 )
END
END;
{
╔═════════════════════════════════════════════════════════════════════════╗
║ PROCEDURE SetEyePos ║
╟─────────────────────────────────────────────────────────────────────────╢
║ Input : 'Eye's' coordinates in the XZ plane, and viewing angle ║
╟─────────────────────────────────────────────────────────────────────────╢
║ Coordinates must be between -WXDim/2..WXDim/2 and -WZDim/2..WZDim/2. ║
║ Coordinates are trasformed to positive, or to range 0..WXDim, 0..WZDim, ║
║ and then they will be multiplied by WorldXZ, in order to move little ║
║ smaller steps (using integer arithmetic, of cource). ║
║ XP and ZP contains the coordinates in 8-bit fixed-point form. ║
╚═════════════════════════════════════════════════════════════════════════╝
}
PROCEDURE SetEyePos( EyeX, EyeZ, Angle : Integer );
BEGIN
WITH EyePA DO
BEGIN
X := EyeX;
Z := EyeZ;
MakeWorldPoint( X, Z );
XP := LongInt( X ) * 256;
ZP := LongInt( Z ) * 256;
YAng := Angle;
END;
END;
BEGIN
Initialize
END.