From dc536cecae07e1e7361278a2147fd567382bce93 Mon Sep 17 00:00:00 2001 From: Cong Date: Mon, 11 Apr 2022 20:40:04 +1000 Subject: [PATCH] Initial commit --- bigfont.pas | 96 ++++ buffer.pas | 565 ++++++++++++++++++++ config.pas | 19 + credits.pas | 118 +++++ dogs.pas | 996 +++++++++++++++++++++++++++++++++++ elements.pas | 218 ++++++++ equip.pas | 492 ++++++++++++++++++ fancy.pas | 101 ++++ game.pas | 1402 ++++++++++++++++++++++++++++++++++++++++++++++++++ gamearea.pas | 565 ++++++++++++++++++++ globals.pas | 378 ++++++++++++++ hallfame.pas | 356 +++++++++++++ ini.pas | 280 ++++++++++ joystick.pas | 106 ++++ keyboard.pas | 252 +++++++++ mainmenu.pas | 716 ++++++++++++++++++++++++++ pics.pas | 677 ++++++++++++++++++++++++ readme.txt | 20 + screen.pas | 560 ++++++++++++++++++++ sounds.pas | 312 +++++++++++ stick.pas | 116 +++++ 21 files changed, 8345 insertions(+) create mode 100644 bigfont.pas create mode 100644 buffer.pas create mode 100644 config.pas create mode 100644 credits.pas create mode 100644 dogs.pas create mode 100644 elements.pas create mode 100644 equip.pas create mode 100644 fancy.pas create mode 100644 game.pas create mode 100644 gamearea.pas create mode 100644 globals.pas create mode 100644 hallfame.pas create mode 100644 ini.pas create mode 100644 joystick.pas create mode 100644 keyboard.pas create mode 100644 mainmenu.pas create mode 100644 pics.pas create mode 100644 readme.txt create mode 100644 screen.pas create mode 100644 sounds.pas create mode 100644 stick.pas diff --git a/bigfont.pas b/bigfont.pas new file mode 100644 index 0000000..371eb98 --- /dev/null +++ b/bigfont.pas @@ -0,0 +1,96 @@ +unit BigFont; + + +interface + + +procedure BigText( x, y : Integer; s : String ); +procedure FancyBigText( x, y : Integer; s : String; dx, dy : Integer; xInc, yInc : Integer ); +function CharWidth : Integer; +function CharHeight : Integer; + + +implementation + + uses {$IFDEF XLIB} XSPX {$ELSE} SPX_VGA {$ENDIF}, + Pics, Fancy; + + +const + + cCapitalA = 140; + cZero = 166; + cDot = 176; + + +function ValidChar( c : Char ) : Boolean; +begin + ValidChar := c in ['A'..'Z', 'a'..'z', '0'..'9', '.']; +end; + + +function PicIndex( c : Char ) : Integer; +begin + if c = '.' then + PicIndex := cDot + else if c in ['0'..'9'] then + PicIndex := Ord( c) - Ord( '0') + cZero + else begin + c := UpCase( c); + PicIndex := Ord( c) - Ord( 'A') + cCapitalA; + end; +end; + + +procedure BigText( x, y : Integer; s : String ); +var i : Integer; + w : Integer; +begin + w := CharWidth; + for i := 1 to Length( s) do + begin + if ValidChar( s[i]) then + ftPut_Clip( x, y, gPics[ PicIndex( s[i])]^, True); + Inc( x, w); + end; +end; + + +procedure FancyBigText( x, y : Integer; s : String; dx, dy : Integer; xInc, yInc : Integer ); +var i : Integer; + w : Integer; +begin + w := CharWidth; + for i := 1 to Length( s) do + begin + if ValidChar( s[i]) then + begin + if (dx <= 0) and (dy <= 0) then + ftPut_Clip( x, y, gPics[ PicIndex( s[i])]^, True) + else + FancyPut( x, y, gPics[ PicIndex( s[i])]^, True, dx, dy); + end; + Inc( dx, xInc); + Inc( dy, yInc); + Inc( x, w); + end; +end; + + +function CharWidth : Integer; +var w, h : Integer; +begin + ImageDims( gPics[ cCapitalA]^, w, h); + CharWidth := w + 1; +end; + + +function CharHeight : Integer; +var w, h : Integer; +begin + ImageDims( gPics[ cCapitalA]^, w, h); + CharHeight := h + 5; +end; + + +end. diff --git a/buffer.pas b/buffer.pas new file mode 100644 index 0000000..537fe62 --- /dev/null +++ b/buffer.pas @@ -0,0 +1,565 @@ +unit Buffer; + + +interface + + uses Globals, Elements; + + +const + + cSplitScreenNever = 0; + cSplitScreenAlways = 1; + cSplitScreenOften = 2; + cSplitScreenSeldom = 3; + + cxSplitOften = 140; + cxSplitSeldom = 250; + + gSplitScreenMode : Byte = cSplitScreenSeldom; + + +function InBuffer1( x, y, offset, w, h : Integer; var xOut, yOut : Integer ) : Boolean; +function InBuffer2( x, y, offset, w, h : Integer; var xOut, yOut : Integer ) : Boolean; +function CharacterVisible( c : PCharacter ) : Boolean; +procedure SetBuffer; +procedure CheckLOS; +procedure DrawBuffer; + + +implementation + + uses SPX_Txt, SPX_Fnc, Pics, GameArea, + SPX_VGA; + + + + +const + + cxViewMax = 10; + cyViewMax = 9; + + cxLittleViewMax = 5; + + cShade1Flag = 256; + cShade2Flag = 512; + cBlocks1Flag = 1024; + cBlocks2Flag = 2048; + + cShadeXFlag = 4096; + cBlocksXFlag = 8192; + + +type + + PosRec = + record + x, y : Integer; + end; + + BufferType = array [0..cxViewMax, 0..cyViewMax] of Word; + + +var + + uBuffer1, + uBuffer2 : BufferType; + + uPos1, + uPos2 : PosRec; + + uSplitScreen : Boolean; + + +procedure DetermineScreen; +var xSplit : Integer; +begin + uSplitScreen := False; + if (gHero1 <> nil) and + (gHero2 <> nil) then + begin + if gSplitScreenMode = cSplitScreenOften then + xSplit := cxSplitOften + else + xSplit := cxSplitSeldom; + if (gSplitScreenMode <> cSplitScreenNever) and + ((gSplitScreenMode = cSplitScreenAlways) or + (Abs( gHero1^.x - gHero2^.x) > xSplit) or + (Abs( gHero1^.y - gHero2^.y) > cMaxVerticalDistance)) then + begin + uSplitScreen := True; + uPos1.x := gHero1^.x - 72; + uPos1.y := gHero1^.y - 90; + uPos2.x := gHero2^.x - 72; + uPos2.y := gHero2^.y - 90; + end + else begin + uPos1.x := (gHero1^.x + gHero2^.x) div 2 - 152; + uPos1.y := (gHero1^.y + gHero2^.y) div 2 - 90; + end; + end + else if gHero1 <> nil then + begin + uPos1.x := gHero1^.x - 152; + uPos1.y := gHero1^.y - 90; + end + else if gHero2 <> nil then + begin + uPos1.x := gHero2^.x - 152; + uPos1.y := gHero2^.y - 90; + end; +end; + + +procedure SetOneBuffer( xMax : Integer; const p : PosRec; var b : BufferType ); +var x, y, bx, by : Integer; + x1, x2, y1, y2 : Integer; +begin + bx := p.x div 32; + by := p.y div 24; + + x1 := 0; + x2 := xMax; + y1 := 0; + y2 := cyViewMax; + if bx < cxMin then + x1 := cxMin - bx; + if by < cyMin then + y1 := cyMin - by; + if bx + xMax > cxMax then + x2 := cxMax - bx; + if by + cyViewMax > cyMax then + y2 := cyMax - by; + + FillChar( b, SizeOf( b), 0); + for x := x1 to x2 do + for y := y1 to y2 do + b[ x, y] := gWorld[ x + bx, y + by]; +end; + + +procedure SetBuffer; +begin + DetermineScreen; + if uSplitScreen then + begin + SetOneBuffer( cxLittleViewMax, uPos1, uBuffer1); + SetOneBuffer( cxLittleViewMax, uPos2, uBuffer2); + end + else + SetOneBuffer( cxViewMax, uPos1, uBuffer1); +end; + + +procedure ShadeBuffer( xMax : Integer; const p : PosRec; var b : BufferType; + cx, cy : LongInt; + blocksFlag, shadeFlag : Word ); + + procedure Test( x, y, dx, dy : Integer ); + begin + if b[ x + dx, y + dy] and (shadeFlag or cNoWalk or cNoWalkLeft) <> 0 then + b[ x, y] := b[ x, y] or (shadeFlag or blocksFlag); + {else if b[ x + dx, y + dy] and (cNoWalk or cNoWalkLeft) <> 0 then + b[ x, y] := b[ x, y] or (shadeFlag or blocksFlag);} + end; + + procedure TestRight( x, y, dx, dy : Integer ); + begin + if b[ x + dx, y + dy] and shadeFlag <> 0 then + b[ x, y] := b[ x, y] or (shadeFlag or blocksFlag) + else if b[ x + dx, y + dy] and (cNoWalk or cNoWalkLeft) <> 0 then + begin + b[ x, y] := b[ x, y] or (shadeFlag or blocksFlag); + b[ x + dx, y + dy] := b[ x + dx, y + dy] or blocksFlag; + end; + end; + + procedure TestVertical( x, y, dx, dy : Integer ); + begin + if b[ x + dx, y + dy] and (shadeFlag or cNoWalk) <> 0 then + b[ x, y] := b[ x, y] or (shadeFlag or blocksFlag); + {if b[ x + dx, y + dy] and shadeFlag <> 0 then + b[ x, y] := b[ x, y] or (shadeFlag or blocksFlag) + else if b[ x + dx, y + dy] and cNoWalk <> 0 then + b[ x, y] := b[ x, y] or (shadeFlag or blocksFlag);} + end; + + procedure TestRightEdge( x, y : Integer ); + begin + if b[ x, y] and cNoWalkLeft <> 0 then + b[ x, y] := b[ x, y] or blocksFlag; + end; + +var x, y : Integer; +begin + cx := cx div 32 - p.x div 32; + cy := cy div 24 - p.y div 24; + + if b[ cx, cy] and cNoWalk <> 0 then + Inc( cy); + + for x := cx - 1 downto 0 do + begin + if cy > 0 then + Test( x, cy-1, 1, 1); + Test( x, cy, 1, 0); + if cy < cyViewMax then + Test( x, cy+1, 1, -1); + end; + for x := cx + 2 to xMax do + begin + if cy > 0 then + TestRight( x, cy-1, -1, 1); + TestRight( x, cy, -1, 0); + if cy < cyViewMax then + TestRight( x, cy+1, -1, -1); + end; + for y := cy - 2 downto 0 do + begin + if cx > 0 then + Test( cx-1, y, 1, 1); + TestVertical( cx, y, 0, 1); + if cx < xMax then + TestVertical( cx+1, y, -1, 1); + end; + for y := cy + 2 to cyViewMax do + begin + if cx > 0 then + Test( cx-1, y, 1, -1); + TestVertical( cx, y, 0, -1); + if cx < xMax then + TestVertical( cx+1, y, -1, -1); + TestRightEdge( xMax, y); + end; + for x := cx - 2 downto 0 do + for y := cy - 2 downto 0 do + Test( x, y, 1, 1); + for x := cx - 2 downto 0 do + for y := cy + 2 to cyViewMax do + Test( x, y, 1, -1); + for x := cx + 2 to xMax do + for y := cy - 2 downto 0 do + TestRight( x, y, -1, 1); + for x := cx + 2 to xMax do + for y := cy + 2 to cyViewMax do + TestRight( x, y, -1, -1); + for x := cx + 1 to xMax do + begin + TestRightEdge( x, 0); + TestRightEdge( x, cyViewMax); + end; + for y := 0 to cyViewMax do + TestRightEdge( xMax, y); +end; + +{ +procedure FixBufferIntermediate( blocks, shadow, blocksFlag, shadeFlag : Word ); +var x, y : Integer; +begin + for x := 0 to cxViewMax do + for y := 0 to cyViewMax do + begin + if gBuffer[ x, y] and shadow = shadow then + gBuffer[ x, y] := (gBuffer[ x, y] and (not shadow)) or shadeFlag + else + gBuffer[ x, y] := (gBuffer[ x, y] and (not shadow)); + if gBuffer[ x, y] and blocks = blocks then + gBuffer[ x, y] := (gBuffer[ x, y] and (not blocks)) or blocksFlag + else + gBuffer[ x, y] := (gBuffer[ x, y] and (not blocks)); + end; +end; +} + + +procedure FixBuffer( max, blocks, shadow : Word; var b : BufferType ); +var x, y : Integer; +begin + for x := 0 to max do + for y := 0 to cyViewMax do + if b[ x, y] and shadow = shadow then + b[ x, y] := cBkgShadow + else if b[ x, y] and (blocks or cNoWalkLeft) = (blocks or cNoWalkLeft) then + begin + case b[ x, y] and 255 of + cBkgRightT: b[ x, y] := cBkgShadowRightT; + cBkgVert: b[ x, y] := cBkgShadowVert; + cBkgDownRight: b[ x, y] := cBkgShadowDownRight; + cBkgDownEnd: b[ x, y] := cBkgShadowDownEnd; + cBkgUpRight: b[ x, y] := cBkgShadowUpRight; + cBkgUpEnd: b[ x, y] := cBkgShadowUpEnd; + end; + end + else + b[ x, y] := b[ x, y] and cBkgFlagMask; +end; + + +procedure CheckLOS; +var shadow, blocks : Word; + dx : Integer; +begin + shadow := 0; + blocks := 0; + if gHero1 <> nil then + with gHero1^ do + begin + case direction of + cDirectionDown, + cDirectionDownLeft, + cDirectionLeft: dx := 3; + cDirectionUp, + cDirectionUpRight, + cDirectionRight: dx := 12; + else dx := 8; + end; + + if uSplitScreen then + begin + ShadeBuffer( cxLittleViewMax, uPos1, uBuffer1, + x + dx, y + 10, cBlocks1Flag, cShade1Flag); + FixBuffer( cxLittleViewMax, cBlocks1Flag, cShade1Flag, uBuffer1); + end + else + ShadeBuffer( cxViewMax, uPos1, uBuffer1, + x + dx, y + 10, cBlocks1Flag, cShade1Flag); + shadow := cShade1Flag; + blocks := cBlocks1Flag; + end; + if gHero2 <> nil then + with gHero2^ do + begin + case direction of + cDirectionDown, + cDirectionDownLeft, + cDirectionLeft: dx := 3; + cDirectionUp, + cDirectionUpRight, + cDirectionRight: dx := 12; + else dx := 8; + end; + if uSplitScreen then + begin + ShadeBuffer( cxLittleViewMax, uPos2, uBuffer2, + x + dx, y + 10, cBlocks2Flag, cShade2Flag); + FixBuffer( cxLittleViewMax, cBlocks2Flag, cShade2Flag, uBuffer2); + end + else + ShadeBuffer( cxViewMax, uPos1, uBuffer1, + x + dx, y + 10, cBlocks2Flag, cShade2Flag); + shadow := shadow or cShade2Flag; + blocks := blocks or cBlocks2Flag; + end; + if not uSplitScreen then + FixBuffer( cxViewMax, blocks, shadow, uBuffer1); +end; + + +procedure DrawOneBuffer( xMax, xOffs, yOffs : Integer; var b : BufferType ); +var x, y, + yMax, + xc, yc : Integer; + offset : Word; + + function GetPic( i : Integer ) : Pointer; + begin + if i in cBkgSpecial then + case i of + + cBkgChaosBox and cBkgFlagMask: + GetPic := gPics[ cBkgAnimated[ cBkgAnimatedChaosBox, (gFrameCounter div 8) and 3]]; + + {cBkgChaosBox2 and cBkgFlagMask: + GetPic := gPics[ cBkgAnimated[ cBkgAnimatedChaosBox2, (gFrameCounter div 16) and 3]];} + + cBkgExit: + {if gTargetsLeft > 0 then + GetPic := gPics[ cExitPic] + else} + GetPic := gPics[ cBkgAnimated[ cBkgAnimatedExit, (gFrameCounter div 4) and 3]]; + + cBkgFloorFan: + GetPic := gPics[ cBkgAnimated[ cBkgAnimatedFan, (gFrameCounter div 4) and 3]]; + + cBkgBlueBox and cBkgFlagMask: + GetPic := gPics[ cBlueBoxPic]; + + cBkgGreyBox and cBkgFlagMask: + GetPic := gPics[ cGreyBoxPic]; + + cBkgBox and cBkgFlagMask: + GetPic := gPics[ cBoxPic]; + + else + GetPic := gBkgPics[ cBkgFloorCrater]; + end + else + GetPic := gBkgPics[ i and cBkgFlagMask]; + end; + +begin + yc := yoffs; + y := 0; + + xc := xOffs; + x := 0; + while x <= xMax do + begin + fPut_Clip( xc, yc, GetPic( b[ x, y])^, False); + Inc( xc, 32); + Inc( x); + end; + Inc( yc, 24); + Inc( y); + + yMax := cyViewMax; + if yc > 8 then + Dec( yMax); + + while y < yMax do + begin + xc := xOffs; + x := 0; + + fPut_Clip( xc, yc, GetPic( b[ x, y])^, False); + Inc( xc, 32); + Inc( x); + offset := Pt( xc, yc); + while x < xMax do + begin + fPutDW( offset, GetPic( b[ x, y])^); + {fPut( xc, yc, gBkgPics[ b[ x, y]]^, False);} + Inc( offset, 32); + Inc( xc, 32); + Inc( x); + end; + fPut_Clip( xc, yc, GetPic( b[ x, y])^, False); + + Inc( yc, 24); + Inc( y); + end; + + if yc < 200 then + begin + xc := xOffs; + x := 0; + while x <= xMax do + begin + fPut_Clip( xc, yc, GetPic( b[ x, y])^, False); + Inc( xc, 32); + Inc( x); + end; + end; +end; + + +procedure UpdateAutoMap( xc, yc, xMax : Integer; var b : BufferType ); +var x, y : Integer; +begin + for x := 0 to xMax do + for y := 0 to cyViewMax do + if b[ x, y] <> cBkgShadow then + if not (b[ x, y] in cBkgHidden) or + (gAutoMap[ x + xc, y + yc] = cBkgShadow) then + gAutoMap[ x + xc, y + yc] := b[ x, y]; +end; + + +procedure DrawBuffer; +begin + if uSplitScreen then + begin + SetClipRange( 0, 0, 158, 199); + DrawOneBuffer( cxLittleViewMax, - (uPos1.x and 31), - (uPos1.y mod 24), uBuffer1); + UpdateAutoMap( uPos1.x div 32, uPos1.y div 24, cxLittleViewMax, uBuffer1); + + SetClipRange( 161, 0, 319, 199); + DrawOneBuffer( cxLittleViewMax, 160 - (uPos2.x and 31), - (uPos2.y mod 24), uBuffer2); + UpdateAutoMap( uPos2.x div 32, uPos2.y div 24, cxLittleViewMax, uBuffer2); + + SetClipRange( 0, 0, 319, 199); + Line( 159, 0, 159, 199, 0); + Line( 160, 0, 160, 199, 0); + end + else begin + SetClipRange( 0, 0, 319, 199); + DrawOneBuffer( cxViewMax, - (uPos1.x and 31), - (uPos1.y mod 24), uBuffer1); + UpdateAutoMap( uPos1.x div 32, uPos1.y div 24, cxViewMax, uBuffer1); + end; +end; + + +function InBuffer1( x, y, offset, w, h : Integer; var xOut, yOut : Integer ) : Boolean; +var xc, yc, xMax : Integer; + b : Word; +begin + InBuffer1 := False; + if uSplitScreen then + SetClipRange( 0, 0, 158, 199) + else + SetClipRange( 0, 0, 319, 199); + xOut := x - uPos1.x; + yOut := y - uPos1.y; + + if uSplitScreen then + xMax := 160 + else + xMax := 320; + if not Range( xOut, yOut, -w, -h, xMax, 199) then + Exit; + xc := (x + w div 2) div 32 - uPos1.x div 32; + yc := (y + offset) div 24 - uPos1.y div 24; + if uSplitScreen then + IFix( xc, 0, cxLittleViewMax) + else + IFix( xc, 0, cxViewMax); + IFix( yc, 0, cxViewMax); + + b := uBuffer1[ xc, yc]; + InBuffer1 := (not (b in cBkgHidden)) or + ((b <> cBkgShadow) and (x and 31 < 16)); +end; + + +function InBuffer2( x, y, offset, w, h : Integer; var xOut, yOut : Integer ) : Boolean; +var x1, y1, x2, y2 : Integer; + b : Word; +begin + InBuffer2 := False; + if not uSplitScreen then + Exit; + + x1 := uPos2.x - w; + y1 := uPos2.y - h; + x2 := uPos2.x + 160; + y2 := uPos2.y + 200; + if not Range( x, y, x1, y1, x2, y2) then + Exit; + x1 := (x + w div 2) div 32 - uPos2.x div 32; + y1 := (y + offset) div 24 - uPos2.y div 24; + IFix( x1, 0, cxLittleViewMax); + IFix( y1, 0, cxViewMax); + + b := uBuffer2[ x1, y1]; + if (not (b in cBkgHidden)) or + ((b <> cBkgShadow) and (x and 31 < 16)) then + begin + InBuffer2 := True; + xOut := 160 + x - uPos2.x; + yOut := y - uPos2.y; + SetClipRange( 161, 0, 319, 199); + end; +end; + + +function CharacterVisible( c : PCharacter ) : Boolean; +var x, y : Integer; +begin + CharacterVisible := + InBuffer1( c^.x, c^.y, 0, cCharacterWidth, cCharacterHeight, x, y) or + InBuffer2( c^.x, c^.y, 0, cCharacterWidth, cCharacterHeight, x, y); +end; + + +end. diff --git a/config.pas b/config.pas new file mode 100644 index 0000000..f57c06c --- /dev/null +++ b/config.pas @@ -0,0 +1,19 @@ +unit Config; + + +interface + + +implementation + + +procedure GetConfiguration; +var s : PSetting; +begin + LoadIniFile( 'DOGS', s); + +end; + + + +end. \ No newline at end of file diff --git a/credits.pas b/credits.pas new file mode 100644 index 0000000..e18539c --- /dev/null +++ b/credits.pas @@ -0,0 +1,118 @@ +unit Credits; + + +interface + + +procedure DisplayCredits; + + +implementation + + uses SPX_VGA, SPX_Txt, SPX_Fnc, Crt, + Keyboard, Screen, Globals, Stick, BigFont, Pics, Sounds; + + + +type + + Credit = + record + color : Byte; + line : String[60]; + end; + +const + + cMaxCredits = 21; + cCredits : array [1..cMaxCredits] of Credit = + ( + (color: cYellow; line: 'CyberDogs v1.0'), + (color: cYellow; line: 'Code and graphics by Ronny Wester'), + (color: cBlack; line: ''), + (color: cOrange; line: 'Written using:'), + (color: cBlack; line: ''), + (color: cRed; line: 'Borland Pascal 7.0'), + (color: cBlack; line: ''), + (color: cRed; line: 'SPX 2.0 by Scott Ramsay'), + (color: cBlack; line: ''), + (color: cRed; line: 'DSMI - Digital Sound and Music Interface'), + (color: cRed; line: 'by Otto Chrons and Jussi Lahdenniemi'), + (color: cOrange; line: 'Playtesters and other helpful people:'), + (color: cBlack; line: ''), + (color: cRed; line: 'Jouni Miettunen'), + (color: cRed; line: 'John Isidoro'), + (color: cRed; line: 'Christian Wagner'), + (color: cRed; line: 'Jan Olof Hendig'), + (color: cRed; line: 'Anders Torlind'), + (color: cRed; line: 'Victor Putz'), + (color: cRed; line: 'Niklas Wester'), + (color: cRed; line: 'Camilla Wester') + ); + +procedure DisplayCredits; + + procedure ClearDisplay; + begin + Bar( 64, 60, 255, 179, cBlack); + end; + + procedure WriteLine( y : Integer; s : String; c : Byte ); + var i : Integer; + x : Integer; + begin + i := 1; + x := 160 - StLen( s) div 2; + while (i <= Length( s)) and not AnyKeyDown and not StickButtonPressed do + begin + PutLetter( x, y, cWhite, s[i]); + VSinc; + VSinc; + PutLetter( x, y, cYellow, s[i]); + VSinc; + VSinc; + PutLetter( x, y, c, s[i]); + Inc( x, StLen( s[i])); + Inc( i); + end; + end; + +var y : Integer; + i : Integer; +begin + FadeOut( 5, gPalette); + SetPageActive( 1); + FillScreen( cSteelPlate); + ClearDisplay; + + BigText( 50, 25, 'CREDITS'); + + if gCreditsSong <> gMenuSong then + PlayMusic( gCreditsSong); + FadeIn( 20, gPalette); + + i := 1; + y := 65; + while not AnyKeyDown and not StickButtonPressed do + begin + while (i <= cMaxCredits) and (y < 175) do + begin + WriteLine( y, cCredits[i].line, cCredits[i].color); + Inc( y, 10); + Inc( i); + end; + if i > cMaxCredits then + i := 1; + if not AnyKeyDown and not StickButtonPressed then + for y := 1 to 25 do + VSinc; + y := 65; + ClearDisplay; + end; + FadeOut( 5, gPalette); + if gCreditsSong <> gMenuSong then + PlayMusic( gMenuSong); +end; + + +end. diff --git a/dogs.pas b/dogs.pas new file mode 100644 index 0000000..cfefe53 --- /dev/null +++ b/dogs.pas @@ -0,0 +1,996 @@ +Program Dogs; + + uses Crt, Strings, + SPX_VGA, + SPX_Fnc, SPX_Txt, + Keyboard, Joystick, Stick, + Pics, Globals, Fancy, BigFont, + GameArea, Screen, Game, Sounds, + MainMenu, Equip, HallFame, + TimeServ, + Elements, Ini; + + +const + + cDogsPics = 'DOGS.PX'; + cDogsIni = 'DOGS'; + + +procedure Intro; +var x, y : Integer; +begin + repeat + x := Random( cxMax - cxMin - 2) + cxMin + 1; + y := Random( cyMax - cyMin - 2) + cyMin + 1; + until gWorld[ x, y] = cBkgFloor; + + if gHero1 <> nil then + begin + gHero1^.direction := 4; + gHero1^.x := x*32; + gHero1^.y := y*24; + SetPosFlag( gHero1); + end; + + if gHero2 <> nil then + begin + gHero2^.direction := 4; + gHero2^.x := x*32 + 16; + gHero2^.y := y*24; + SetPosFlag( gHero2); + end; + + Frame; + FadeIn( 15, gPalette); +end; + + +function MissionBonusTime : Integer; +var time : Integer; +begin + time := 60 + gObjectsToCollect*7 + gMinimumKills*3 + gTargetsLeft*15; + if gPlayerData[0].playing and gPlayerData[1].playing then + time := (2*time) div 3; + MissionBonusTime := time; +end; + + +procedure MissionIntro( mission : Integer ); +var time : Integer; + + procedure MissionOrders; + begin + if gObjectsToCollect > 0 then + begin + fPut( 64, 24*2 + 40, gBkgPics[ cBkgFloorDocs]^, False); + fPut( 96, 24*2 + 40, gBkgPics[ cBkgFloorFolder]^, False); + fPut( 128, 24*2 + 40, gBkgPics[ cBkgFloorDisk]^, False); + fPut( 160, 24*2 + 40, gBkgPics[ cBkgFloorCircuit]^, False); + fPut( 192, 24*2 + 40, gBkgPics[ cBkgFloorTeddy]^, False); + end; + if gMinimumKills > 0 then + begin + fPut( 64, 24*3 + 40, gBkgPics[ cBkgFloorSkull]^, False); + fPut( 128, 24*3 + 40, gBkgPics[ cBkgFloorBlood]^, False); + fPut( 192, 24*3 + 40, gBkgPics[ cBkgFloorBlood]^, False); + fPut( 32*3, 40, gBkgPics[ cBkgHorz1 and cBkgFlagMask]^, False); + fPut( 32*4, 40, gBkgPics[ cBkgHorz2 and cBkgFlagMask]^, False); + fPut( 32*5, 40, gBkgPics[ cBkgHorz4 and cBkgFlagMask]^, False); + fPut( 32*6, 40, gBkgPics[ cBkgHorz3 and cBkgFlagMask]^, False); + end; + if gTargetsLeft > 0 then + begin + fPut( 224, 24*3 + 40, gBkgPics[ cBkgChaosBoxRemains]^, False); + fPut( 224, 24*4 + 40, gBkgPics[ cBkgFloorCrater]^, False); + end; + if mission < 9 then + DrawLetter( 80, 45, cWhite, cBlack, 'Mission #'+St(mission+1)) + else + DrawLetter( 80, 45, cBrightRed, cBlack, 'The Final Showdown!'); + if gObjectsToCollect > 0 then + DrawLetter( 70, 55, cYellow, cBlack, 'Retrieve at least '+St( gObjectsToCollect)+' objects'); + if gMinimumKills > 0 then + DrawLetter( 70, 65, cBrightRed, cBlack, 'Eliminate at least '+St(gMinimumKills)+' enemies'); + if gTargetsLeft > 0 then + DrawLetter( 70, 75, cOrange, cBlack, 'Blow up '+St( gTargetsLeft)+' enemy structure(s)'); + time := MissionBonusTime; + DrawLetter( 70, 85, cWhite, cBlack, 'Bonus deadline: '+ St( time div 60) + ':' + Lz( time mod 60, 2)); + end; + + procedure DrawBaddie( count, face, body, percentage : Integer ); + begin + DrawCharacter( 32 + 32*(1 + count) + 10, 24*3 + 40, + cDirectionDown, cDirectionDown, + face, body, cGunDogs, 0, 0, cGunIdle); + DrawLetter( 32 + 32*(1 + count) + 12, 24*3 + 60, cWhite, cBlack, St( percentage) + '%'); + end; + +var x, y : Integer; + count : Integer; +begin + SetPageActive( 1); + FillScreen( cSteelPlate); + + for x := 1 to 8 do + for y := 0 to 4 do + fPut( x*32, y*24 + 40, gBkgPics[ cBkgFloor]^, False); + + fPut( 32, 24*4 + 40, gBkgPics[ cBkgDownEnd and cBkgFlagMask]^, False); + fPut( 32, 24*3 + 40, gBkgPics[ cBkgVert and cBkgFlagMask]^, False); + fPut( 32, 24*2 + 40, gBkgPics[ cBkgVert and cBkgFlagMask]^, False); + fPut( 32, 24*1 + 40, gBkgPics[ cBkgVert and cBkgFlagMask]^, False); + fPut( 32, 40, gBkgPics[ cBkgUpLeft and cBkgFlagMask]^, False); + for x := 2 to 6 do + begin + fPut( 32*x, 40, gBkgPics[ cBkgHorz and cBkgFlagMask]^, False); + fPut( 32*x, 24 + 40, gBkgPics[ cBkgBelowWall and cBkgFlagMask]^, False); + end; + fPut( 32*7, 40, gBkgPics[ cBkgRightEnd and cBkgFlagMask]^, False); + fPut( 32*7, 24 + 40, gBkgPics[ cBkgBelowWall and cBkgFlagMask]^, False); + fPut( 32*8, 40, gBkgPics[ cBkgRightOfWall and cBkgFlagMask]^, False); + fPut( 32*8, 24 + 40, gBkgPics[ cBkgBelowAndRightOfWall and cBkgFlagMask]^, False); + + MissionOrders; + + count := 0; + if gBaddieProbs[ cBaddieGoon] > 0 then + begin + DrawBaddie( count, cFaceGrunt, cBrownBody, gBaddieProbs[ cBaddieGoon]); + Inc( count); + end; + if gBaddieProbs[ cBaddieMeanGoon] > 0 then + begin + DrawBaddie( count, cFaceBlondGrunt, cBrownBody, gBaddieProbs[ cBaddieMeanGoon]); + Inc( count); + end; + if gBaddieProbs[ cBaddieKiller] > 0 then + begin + DrawBaddie( count, cFaceGrunt2, cRedBody, gBaddieProbs[ cBaddieKiller]); + Inc( count); + end; + if gBaddieProbs[ cBaddieRobot] > 0 then + begin + DrawBaddie( count, cFaceMechGrunt, cGreyBody, gBaddieProbs[ cBaddieRobot]); + Inc( count); + end; + if gBaddieProbs[ cBaddieLittleBad] > 0 then + begin + DrawBaddie( count, cFaceGrunt2, cBlackBody, gBaddieProbs[ cBaddieLittleBad]); + Inc( count); + end; + if gBaddieProbs[ cBaddieBigBad] > 0 then + begin + DrawBaddie( count, cFaceBigBadGuy, cBlackBody, gBaddieProbs[ cBaddieBigBad]); + Inc( count); + end; + + FadeIn( 20, gPalette); + while not (AnyKeyDown or StickButtonPressed) do; + while (AnyKeyDown or StickButtonPressed) do; + FadeOut( 5, gPalette); +end; + + +procedure ClearPlayerData( var data : PlayerData ); +begin + FillChar( data.total, SizeOf( data.total), 0); + data.missions := 0; + FillChar( data.weapons, SizeOf( data.weapons), 0); + data.lives := 2; + data.armor := 20; + data.closeCombat := 1; + data.cash := 10000; +end; + + +procedure UpdatePlayerData( index : Integer ); +var i : Integer; +begin + with gPlayerData[ index] do + begin + { + FillChar( weapons, SizeOf( weapons), 0); + armor := 15; + lives := 2; + closeCombat := 1;} + end; +end; + + +procedure MissionSummary( var mission : Integer ); + + procedure MissionObjectives( var success : Boolean ); + var objects, kills, targets : Integer; + begin + objects := 0; + kills := 0; + targets := 0; + if gPlayerData[0].playing then + begin + Inc( objects, gPlayerData[0].mission.objects); + Inc( kills, gPlayerData[0].mission.kills); + Inc( targets, gPlayerData[0].mission.targets); + end; + if gPlayerData[1].playing then + begin + Inc( objects, gPlayerData[1].mission.objects); + Inc( kills, gPlayerData[1].mission.kills); + Inc( targets, gPlayerData[1].mission.targets); + end; + if gObjectsToCollect > 0 then + begin + DrawLetter( 70, 45, cYellow, cBlack, 'Objects collected: ' + St( objects)); + DrawLetter( 170, 45, cYellow, cBlack, 'Required: ' + St( gObjectsToCollect)); + end; + if gMinimumKills > 0 then + begin + DrawLetter( 70, 55, cBrightRed, cBlack, 'Enemies eliminated: ' + St( kills)); + DrawLetter( 170, 55, cBrightRed, cBlack, 'Required: ' + St( gMinimumKills)); + end; + if (targets > 0) or (gTargetsLeft > 0) then + begin + DrawLetter( 70, 65, cOrange, cBlack, 'Targets destroyed: ' + St( targets)); + DrawLetter( 170, 65, cOrange, cBlack, 'Required: ' + St( targets + gTargetsLeft)); + end; + success := (objects >= gObjectsToCollect) and (kills >= gMinimumKills) and (gTargetsLeft <= 0); + end; + + procedure OutputResult( c : PCharacter; x, index : Integer; success : Boolean ); + var value : LongInt; + s : String; + begin + if gPlayerData[ index].playing then + begin + with cHeroInfo[ gPlayerData[ index].hero] do + if index = 0 then + DrawCharacter( 35, 50, + cDirectionDown, cDirectionDown, + face, body, cGunDogs, 0, 0, cGunIdle) + else + DrawCharacter( 285, 50, + cDirectionDown, cDirectionDown, + face, body, cGunDogs, 0, 0, cGunIdle); + with gPlayerData[ index].mission do + begin + if gPlayerData[ index].completed then + DrawLetter( x, 80, cWhite, cBlack, 'TIME: ' + St( time div 60) + ':' + Lz( time mod 60, 2)); + DrawLetter( x, 88, cWhite, cBlack, 'SCORE: ' + St( score)); + DrawLetter( x, 96, cWhite, cBlack, 'OBJECTS: ' + St( objects)); + DrawLetter( x, 104, cWhite, cBlack, 'KILLS: ' + St( kills)); + DrawLetter( x, 112, cWhite, cBlack, 'DEMOLITION: ' + St( demolition) + 'Cr'); + s := 'ACCURACY: ' + St( hits) + '/' + St( shotsFired); + if shotsFired > 0 then + begin + value := (100*hits) div shotsFired; + s := s + ', ' + St( value) + '%'; + if value > 50 then + begin + s := s + ' ' + St( (value-50)*shotsFired) + 'Cr'; + Inc( gPlayerData[ index].cash, (value-50)*shotsFired); + end; + end; + DrawLetter( x, 120, cWhite, cBlack, s); + end; + if success and gPlayerData[ index].completed then + begin + DrawLetter( x + 50, 104, cWhite, cBlack, 'x50 = ' + St( gPlayerData[ index].mission.kills*50) + 'Cr'); + DrawLetter( x + 50, 96, cWhite, cBlack, 'x100 = ' + St( gPlayerData[ index].mission.objects*100) + 'Cr'); + Inc( gPlayerData[ index].cash, gPlayerData[ index].mission.kills*50); + Inc( gPlayerData[ index].cash, gPlayerData[ index].mission.objects*100); + + Inc( gPlayerData[ index].cash, 1000); + value := (MissionBonusTime - gPlayerData[ index].mission.time)*5; + if value > 0 then + begin + DrawLetter( x, 128, cOrange, cBlack, 'TIME BONUS: ' + St( value div 5) + 'secs x50 = ' + St( value*10)+'Cr'); + Inc( gPlayerData[ index].mission.score, value); + Inc( gPlayerData[ index].cash, value*10); + end; + Inc( gPlayerData[ index].missions); + end; + Inc( gPlayerData[ index].cash, gPlayerData[ index].mission.demolition); + + Inc( gPlayerData[ index].total.time, gPlayerData[ index].mission.time); + Inc( gPlayerData[ index].total.score, gPlayerData[ index].mission.score); + Inc( gPlayerData[ index].total.objects, gPlayerData[ index].mission.objects); + Inc( gPlayerData[ index].total.kills, gPlayerData[ index].mission.kills); + Inc( gPlayerData[ index].total.closeCombat, gPlayerData[ index].mission.closeCombat); + Inc( gPlayerData[ index].total.demolition, gPlayerData[ index].mission.demolition div 10); + Inc( gPlayerData[ index].total.shotsFired, gPlayerData[ index].mission.shotsFired); + Inc( gPlayerData[ index].total.hits, gPlayerData[ index].mission.hits); + Inc( gPlayerData[ index].total.hitsTaken, gPlayerData[ index].mission.hitsTaken); + DrawLetter( x, 140, cGray, cBlack, 'MISSIONS: ' + St( gPlayerData[ index].missions)); + if gPlayerData[ index].missions >= 10 then + begin + DrawLetter( x + 50, 140, cYellow, cBlack, 'Campaign Bonus: ' + St( gPlayerData[ index].total.score div 3)); + Inc( gPlayerData[ index].total.score, gPlayerData[ index].total.score div 3); + gPlayerData[ index].completed := False; + end; + with gPlayerData[ index].total do + begin + DrawLetter( x, 148, cGray, cBlack, 'Score: ' + St( score)); + DrawLetter( x + 70, 148, cGray, cBlack, 'Time: ' + St( time div 60) + ':' + Lz( time mod 60, 2)); + DrawLetter( x, 156, cGray, cBlack, 'Objects: ' + St( objects)); + DrawLetter( x, 164, cGray, cBlack, 'Kills: ' + St( kills)); + DrawLetter( x + 70, 164, cGray, cBlack, 'Ninja: ' + St( closeCombat)); + DrawLetter( x, 172, cGray, cBlack, 'Demolition: ' + St( demolition)); + if shotsFired > 0 then + DrawLetter( x + 70, 172, cGray, cBlack, 'Accuracy: ' + St( (100*hits) div shotsFired) + '%'); + end; + + DrawLetter( x, 180, cYellow, cBlack, 'Cash: ' + St( gPlayerData[ index].cash) + 'Cr'); + end; + end; + + procedure HallOfFameCandidate( index : Integer ); + begin + if not gPlayerData[ index].playing then + Exit; + + if not gPlayerData[ index].completed then + begin + ApplyForHallOfFame( index); + ClearPlayerData( gPlayerData[ index]); + end + else + UpdatePlayerData( index); + end; + +var won : Boolean; +begin + FadeOut( 10, gPalette); + SetPageActive( 1); + FillScreen( cSteelPlate); + + MissionObjectives( won); + if gPlayerData[ 0].completed or gPlayerData[ 1].completed then + begin + if won then + begin + if mission < 9 then + BigText( 50, 20, 'SUCCESS') + else + BigText( 50, 20, 'VICTORY'); + PlayMusic( gSuccessSong); + end + else begin + BigText( 30, 20, 'INCOMPLETE'); + PlayMusic( gFailureSong); + end; + end + else begin + won := False; + BigText( 60, 20, 'DEFEAT'); + PlayMusic( gFailureSong); + end; + + OutputResult( gHero1, 25, 0, won); + OutputResult( gHero2, 175, 1, won); + + FadeIn( 10, gPalette); + while not (AnyKeyDown or StickButtonPressed) do; + + HallOfFameCandidate( 0); + HallOfFameCandidate( 1); + + FadeOut( 10, gPalette); + if won then + Inc( mission); +end; + + + +procedure SetPlayerData( c : PCharacter; var data : PlayerData ); +begin + data.completed := False; + + if c = nil then + Exit; + + c^.gunPic := cGunDogs; + c^.face := cHeroInfo[ data.hero].face; + c^.body := cHeroInfo[ data.hero].body; + c^.gunPos := cGunIdle; + c^.gun.kind := data.weapons[0].kind; + c^.gun.ammo := data.weapons[0].ammo; + c^.armor := data.armor; + c^.lives := data.lives; + c^.closeCombat := data.closeCombat; + + c^.direction := 4; + c^.faceDir := 4; + c^.speed := 8; +end; + + + +procedure SetupPlayers; +var cash : LongInt; +begin + FillChar( gPlayerData[0].mission, SizeOf( gPlayerData[0].mission), 0); + FillChar( gPlayerData[1].mission, SizeOf( gPlayerData[1].mission), 0); + + if gPlayerData[0].playing then + gHero1 := AddCharacter( 0); + if gPlayerData[1].playing then + gHero2 := AddCharacter( 1); + + if gPlayerData[0].playing and + gPlayerData[1].playing and + (g2PlayerPool and cPoolCash <> 0) then + begin + cash := gPlayerData[0].cash + gPlayerData[1].cash; + GetEquipment( 0, cash); + GetEquipment( 1, cash); + gPlayerData[0].cash := cash div 2; + gPlayerData[1].cash := cash - gPlayerData[0].cash; + end + else begin + GetEquipment( 0, gPlayerData[0].cash); + GetEquipment( 1, gPlayerData[1].cash); + end; + + SetPlayerData( gHero1, gPlayerData[0]); + SetPlayerData( gHero2, gPlayerData[1]); +end; + + +procedure InitializeGame; +begin + gPlayerData[0].playing := True; + gPlayerData[1].playing := False; + gPlayerData[0].hero := cHeroIce; + gPlayerData[1].hero := cHeroWarBaby; + ClearPlayerData( gPlayerData[0]); + ClearPlayerData( gPlayerData[1]); +end; + + +procedure SetupAutoMap; +var x, y : Integer; + c : PCharacter; +begin + FillChar( gAutoMap, SizeOf( gAutoMap), cBkgShadow); + for x := cxMin to cxMax do + for y := cxMin to cxMax do + if (gWorld[ x, y] in gMissionTargets) or + (gWorld[ x, y] = cBkgExit) then + gAutoMap[ x, y] := gWorld[ x, y] and cBkgFlagMask; + c := gCharacters; + while c <> nil do + begin + if c^.isTarget then + begin + x := c^.x div cwWorldMap; + y := (c^.y + 10) div chWorldMap; + gAutoMap[ x, y] := cBkgChaosBox and cBkgFlagMask; + end; + c := c^.next; + end; +end; + + +procedure SetupCampaign( var params : WorldParams ); +begin + RandSeed := gCampaign; + with params do + begin + architectureStyle := Random( cArchitectureStyleMax + 1); + wallCount := Random( 500) + 200; + wallLength := Random( 5)*2 + 4; + roomCount := Random( 40) + 20; + detailDensity := Random( 40) + 20; + end; +end; + + +procedure SetupMission( params : WorldParams; mission : Integer; + var movingTargets : Integer ); +var targets, objects : Integer; + percentage : Integer; + architecture : Integer; +begin + gBadGuyCount := 5 + 4*mission; + if gPlayerData[0].playing and gPlayerData[1].playing then + Inc( gBadGuyCount, gBadGuyCount div 2); + if gBadGuyCount > 40 then + gBadGuyCount := 40; + gMissionTargets := [cBkgChaosBox]; + RandSeed := gCampaign + 10*mission; + + repeat + architecture := Random( cArchitectureMax + 1); + until cArchitectures[ architecture].style = params.architectureStyle; + BuildBkgPics( cArchitectures[ architecture], gPalette); + + gObjectsToCollect := 0; + gTargetsLeft := 0; + if (mission > 1) and (Random( 10) < 3) then + gMinimumKills := 30 + 10*mission + else + gMinimumKills := 0; + targets := Random( 1 + mission div 3); + movingTargets := 0; + objects := 6 + 2*mission; + if gPlayerData[0].playing and gPlayerData[1].playing then + begin + Inc( targets, targets div 2); + objects := 2*objects; + end; + if gMinimumKills > 0 then + begin + if Random( 10) < 5 then + objects := 0 + else begin + objects := objects div 2; + gMinimumKills := gMinimumKills div 2; + end; + end; + BuildWorld( params, mission, targets, objects); + + FillChar( gBaddieProbs, SizeOf( gBaddieProbs), 0); + percentage := 10*mission; + if percentage > 100 then + percentage := 100; + gBaddieProbs[ cBaddieGoon] := 100 - percentage; + case Random( 7 + mission) of + 0: gBaddieProbs[ cBaddieMeanGoon] := percentage; + 1: begin + gBaddieProbs[ cBaddieMeanGoon] := percentage div 2; + gBaddieProbs[ cBaddieKiller] := percentage div 2; + end; + 2: begin + gBaddieProbs[ cBaddieMeanGoon] := percentage div 2; + gBaddieProbs[ cBaddieRobot] := percentage div 2; + end; + 3: begin + gBaddieProbs[ cBaddieRobot] := percentage div 2; + gBaddieProbs[ cBaddieKiller] := percentage div 2; + end; + 4: begin + gBaddieProbs[ cBaddieMeanGoon] := percentage div 3; + gBaddieProbs[ cBaddieRobot] := percentage div 3; + gBaddieProbs[ cBaddieKiller] := percentage div 3; + end; + 5: gBaddieProbs[ cBaddieRobot] := percentage; + 6: gBaddieProbs[ cBaddieKiller] := percentage; + 7: begin + gBaddieProbs[ cBaddieRobot] := percentage div 2; + gBaddieProbs[ cBaddieLittleBad] := percentage div 2; + end; + 8: begin + gBaddieProbs[ cBaddieMeanGoon] := percentage div 3; + gBaddieProbs[ cBaddieRobot] := percentage div 3; + gBaddieProbs[ cBaddieKiller] := percentage div 3; + end; + 9, 10, 11, 12: begin + gBaddieProbs[ cBaddieMeanGoon] := percentage div 5; + gBaddieProbs[ cBaddieRobot] := percentage div 5; + gBaddieProbs[ cBaddieKiller] := percentage div 5; + gBaddieProbs[ cBaddieLittleBad] := percentage div 5; + gBaddieProbs[ cBaddieBigBad] := percentage div 5; + end; + 12: gBaddieProbs[ cBaddieLittleBad] := percentage; + 13: begin + gBaddieProbs[ cBaddieMeanGoon] := percentage div 3; + gBaddieProbs[ cBaddieKiller] := percentage div 3; + gBaddieProbs[ cBaddieLittleBad] := percentage div 3; + end; + 14: begin + gBaddieProbs[ cBaddieBigBad] := percentage div 3; + gBaddieProbs[ cBaddieRobot] := (2*percentage) div 3; + end; + 15: begin + gBaddieProbs[ cBaddieLittleBad] := percentage div 3; + gBaddieProbs[ cBaddieBigBad] := percentage div 3; + gBaddieProbs[ cBaddieKiller] := percentage div 3; + end; + 16: begin + gBaddieProbs[ cBaddieRobot] := percentage div 3; + gBaddieProbs[ cBaddieLittleBad] := percentage div 3; + gBaddieProbs[ cBaddieBigBad] := percentage div 3; + end; + else + gBaddieProbs[ cBaddieKiller] := percentage; + end; + + Randomize; + { + SetPageActive( 1); + PutLetter( 10, 50, cWhite, 'SetupMission done.'); + + FadeOut( 63, gPalette); + } +end; + + +procedure SetupGame; +begin + ClearAllPosFlags; +end; + + +procedure SetupBadGuys( bigBads : Integer ); +var i : Integer; +begin + i := 100*bigBads; + while (bigBads > 0) and (i > 0) do + begin + if MakeBadGuy( cBaddieBigBad, True) then + begin + Inc( gTargetsLeft); + Dec( bigBads); + end; + Dec( i); + end; + for i := 1 to gBadGuyCount do + MakeAnyBadGuy; +end; + + +procedure RetrieveIniSettings; + + procedure VerifyStick( i : Integer ); + begin + if StickLeft( i) or StickRight( i) or StickUp( i) or StickDown( i) then + begin + WriteLn( 'Stick ', i, ' may require recalibration...stick disabled'); + Write( 'Neutral: ', gStickLeft[i], ' < x < ', gStickRight[i]); + WriteLn( ', ', gStickUp[i], ' < y < ', gStickDown[i]); + WriteLn( 'Currently: x = ', gSticks[i].x, ', y = ', gSticks[i].y); + if hero1Keys.stick = i then + hero1Keys.stick := 0; + if hero2Keys.stick = i then + hero2Keys.stick := 0; + Write( 'Press Enter...'); ReadLn; + end + else + WriteLn( 'Stick ', i, ' is in neutral position'); + end; + +var i : Integer; + v : PValue; + song : PLevelSong; +begin + WriteLn( 'Loading INI...'); + gIniSettings := nil; + LoadIniFile( 'DOGS', gIniSettings); + + with hero1Keys do + begin + up := FindIniNumber( gIniSettings, 'Player1', 'Up', up); + down := FindIniNumber( gIniSettings, 'Player1', 'Down', down); + left := FindIniNumber( gIniSettings, 'Player1', 'Left', left); + right := FindIniNumber( gIniSettings, 'Player1', 'Right', right); + shoot := FindIniNumber( gIniSettings, 'Player1', 'Shoot', shoot); + switch := FindIniNumber( gIniSettings, 'Player1', 'Switch', switch); + stick := FindIniNumber( gIniSettings, 'Player1', 'Stick', stick); + end; + gStickLeft[1] := FindIniNumber( gIniSettings, 'Player1', 'StickLeft', gStickLeft[1]); + gStickRight[1] := FindIniNumber( gIniSettings, 'Player1', 'StickRight', gStickRight[1]); + gStickUp[1] := FindIniNumber( gIniSettings, 'Player1', 'StickUp', gStickUp[1]); + gStickDown[1] := FindIniNumber( gIniSettings, 'Player1', 'StickDown', gStickDown[1]); + with hero2Keys do + begin + up := FindIniNumber( gIniSettings, 'Player2', 'Up', up); + down := FindIniNumber( gIniSettings, 'Player2', 'Down', down); + left := FindIniNumber( gIniSettings, 'Player2', 'Left', left); + right := FindIniNumber( gIniSettings, 'Player2', 'Right', right); + shoot := FindIniNumber( gIniSettings, 'Player2', 'Shoot', shoot); + switch := FindIniNumber( gIniSettings, 'Player2', 'Switch', switch); + stick := FindIniNumber( gIniSettings, 'Player2', 'Stick', stick); + end; + gStickLeft[2] := FindIniNumber( gIniSettings, 'Player2', 'StickLeft', gStickLeft[2]); + gStickRight[2] := FindIniNumber( gIniSettings, 'Player2', 'StickRight', gStickRight[2]); + gStickUp[2] := FindIniNumber( gIniSettings, 'Player2', 'StickUp', gStickUp[2]); + gStickDown[2] := FindIniNumber( gIniSettings, 'Player2', 'StickDown', gStickDown[2]); + + PollSticks; + VerifyStick( 1); + VerifyStick( 2); + + for i := 1 to cSoundMax do + gSoundFiles[i] := FindIniString( gIniSettings, 'Sound', gSoundFiles[i], ''); + + gMenuSong := FindIniString( gIniSettings, 'Music', 'Menu', gMenuSong); + gCreditsSong := FindIniString( gIniSettings, 'Music', 'Credits', gMenuSong); + gFailureSong := FindIniString( gIniSettings, 'Music', 'Failure', gMenuSong); + gSuccessSong := FindIniString( gIniSettings, 'Music', 'Success', gMenuSong); + gHallOfFameSong := FindIniString( gIniSettings, 'Music', 'Famous', gMenuSong); + + v := GetIniValues( gIniSettings, 'Music'); + while v <> nil do + begin + if Equal( v^.key, 'Level') then + begin + New( song); + song^.song := v^.value; + song^.next := gLevelSongs; + gLevelSongs := song; + end; + v := v^.next; + end; + WriteLn( 'OK'); +end; + + +procedure Initialize; +var palette : RGBList; + i : Integer; +begin + InitSticks; + RetrieveIniSettings; + Randomize; + InitializeSound( + FindIniNumber( gIniSettings, 'Sound', 'Irq', -1), + FindIniNumber( gIniSettings, 'Sound', 'DMA', -1), + FindIniBoolean( gIniSettings, 'Sound', 'Quality', False), + FindIniBoolean( gIniSettings, 'Sound', '486', False), + FindIniBoolean( gIniSettings, 'Sound', 'Off', False)); + OpenMode( 2); + FillChar( palette, SizeOf( palette), 0); + fSetColors( palette); + InstallKbdHandler; + SetClipRange( 0, 0, 319, 199); + for i := 0 to cBkgPicMax do + GetMem( gBkgPics[i], BuffSize( 32, 24)); + New( gCharacterMap); + New( gStructureMap); + New( gNoWalk); +end; + + +procedure SetIniSettings; +begin + WriteLn( 'Updating settings...'); + with hero1Keys do + begin + SetIniNumber( gIniSettings, 'Player1', 'Up', up); + SetIniNumber( gIniSettings, 'Player1', 'Down', down); + SetIniNumber( gIniSettings, 'Player1', 'Left', left); + SetIniNumber( gIniSettings, 'Player1', 'Right', right); + SetIniNumber( gIniSettings, 'Player1', 'Shoot', shoot); + SetIniNumber( gIniSettings, 'Player1', 'Switch', switch); + SetIniNumber( gIniSettings, 'Player1', 'Stick', stick); + end; + SetIniNumber( gIniSettings, 'Player1', 'StickLeft', gStickLeft[1]); + SetIniNumber( gIniSettings, 'Player1', 'StickRight', gStickRight[1]); + SetIniNumber( gIniSettings, 'Player1', 'StickUp', gStickUp[1]); + SetIniNumber( gIniSettings, 'Player1', 'StickDown', gStickDown[1]); + with hero2Keys do + begin + SetIniNumber( gIniSettings, 'Player2', 'Up', up); + SetIniNumber( gIniSettings, 'Player2', 'Down', down); + SetIniNumber( gIniSettings, 'Player2', 'Left', left); + SetIniNumber( gIniSettings, 'Player2', 'Right', right); + SetIniNumber( gIniSettings, 'Player2', 'Shoot', shoot); + SetIniNumber( gIniSettings, 'Player2', 'Switch', switch); + SetIniNumber( gIniSettings, 'Player2', 'Stick', stick); + end; + SetIniNumber( gIniSettings, 'Player2', 'StickLeft', gStickLeft[2]); + SetIniNumber( gIniSettings, 'Player2', 'StickRight', gStickRight[2]); + SetIniNumber( gIniSettings, 'Player2', 'StickUp', gStickUp[2]); + SetIniNumber( gIniSettings, 'Player2', 'StickDown', gStickDown[2]); + WriteLn( 'Saving DOGS.INI...'); + WriteIniFile( 'DOGS', gIniSettings); + WriteLn( 'Cleaning up...'); + DisposeSettings( gIniSettings); +end; + + +procedure CleanUp; +var i : Integer; +begin + RemoveKbdHandler; + CloseMode; + WriteLn( 'Closing down sound...'); + CloseDownSound; + WriteLn( 'Freeing memory...'); + for i := 0 to cBkgPicMax do + FreeMem( gBkgPics[i], BuffSize( 32, 24)); + Dispose( gCharacterMap); + Dispose( gStructureMap); + Dispose( gNoWalk); + SetIniSettings; + WriteLn( 'All done!'); +end; + + +procedure GameCycles; +var i, count : Integer; +begin + Inc( gMissionTime); + gCyclesPerFrame := 1; + while gVBlankCounter > 4 do + begin + Inc( gCyclesPerFrame); + UpdateCharacters( False, False); + MoveAllCharacters; + MoveBullets( False); + Dec( gVBlankCounter, 4); + Inc( gMissionTime); + end; + {gVBlankCounter := 0;} +end; + + +{$F+} +procedure VBlankCounter; interrupt; +begin + Inc( gVBlankCounter); +end; +{$F-} + +procedure MainLoop; +var fastPhaseMask, + slowPhaseMask : Word; + fastPhase, slowPhase : Boolean; + done : Boolean; + mission : Integer; + params : WorldParams; + bigBaddies : Integer; +begin + {fastPhaseMask := 3; + slowPhaseMask := 7;} + + mission := 0; + SetupCampaign( params); + done := False; + repeat + gGameOver := False; + + SetupMission( params, mission, bigBaddies); + MissionIntro( mission); + + SetupPlayers; + SetupGame; + + SetupBadGuys( 0); + SetUpAutoMap; + + Intro; + PlayMusic( GetNextLevelSong); + gMissionTime := 0; + gFrameCounter := 0; + gVBlankCounter := 0; + while not (gGameOver or done) do + begin + if gCyclesPerFrame = 1 then + fastPhaseMask := 3 + else if gCyclesPerFrame < 4 then + fastPhaseMask := 1 + else + fastPhaseMask := 0; + if gCyclesPerFrame = 1 then + slowPhaseMask := 7 + else if gCyclesPerFrame < 4 then + slowPhaseMask := 3 + else if gCyclesPerFrame < 8 then + slowPhaseMask := 1 + else + slowPhaseMask := 0; + fastPhase := gFrameCounter and fastPhaseMask = 0; + slowPhase := gFrameCounter and slowPhaseMask = 0; + + UpdateCharacters( fastPhase, slowPhase); + GetPlayerCommands( slowPhase); + MoveBadGuys( slowPhase); + MoveBullets( fastPhase); + UpdateExplosions( fastPhase); + GameCycles; + Frame; + DoSoundEffects; + if gFrameCounter and 63 = 0 then + RelocateBadGuys; + + if KeyDown( keyEsc) then + begin + while KeyDown( keyEsc) do; + SetPageActive( 1); + DrawLetter( 60, 100, cWhite, cBlack, 'Press Esc again to quit, any other key to continue'); + while not AnyKeyDown do; + if KeyDown( keyEsc) then + begin + ClearPlayerData( gPlayerData[ cIdHero1]); + ClearPlayerData( gPlayerData[ cIdHero2]); + done := True; + end; + gVBlankCounter := 0; + end; + end; + + FadeOut( 1, gPalette); + if gGameOver then + MissionSummary( mission); + RemoveAllElements; + until done or not (gPlayerData[ cIdHero1].completed or gPlayerData[ cIdHero2].completed); + if not done then + DisplayHallOfFame; + PlayMusic( gMenuSong); +end; + + +procedure TitleScreen; +begin + FillScreen( cSteelPlate); + BigText( 70, 20, 'CYBERDOGS'); + BigText( 120, 45, 'v1.0'); + DrawLetter( 110, 100, cWhite, cBlack, '(C) 1994 Ronny Wester'); + DrawLetter( 110, 110, cYellow, cBlack, 'This game is freeware!'); + DrawLetter( 125, 120, cOrange, cBlack, 'Press any key'); + FadeIn( 20, gPalette); + while not AnyKeyDown do; + while AnyKeyDown do; + FadeOut( 20, gPalette); +end; + + +procedure ParseParameters; +var i : Integer; + s : String; + err : Integer; +begin + for i := 1 to ParamCount do + begin + s := ParamStr(i); + if (Length( s) >= 2) and (s[1] in [ '/', '-']) then + case s[2] of + 's': gVSync := False; + 'c': begin + Delete( s, 1, 2); + if s <> '' then + Val( s, gCampaign, err) + else begin + Randomize; + gCampaign := Random( 1024); + end; + end; + end; + end; +end; + + +var oldMode : Word; + +begin + oldMode := LastMode; + if Hi( WindMax) > 25 then + oldMode := oldMode or Font8x8; + + if test8086 < 2 then + begin + WriteLn( 'This game requires 386+. Sorry.'); + Exit; + end; + ParseParameters; + + Initialize; + + LoadPics( cDogsPics, gPics, cMaxPics, gPalette); + with gPalette[0] do + begin + red := 0; + green := 0; + blue := 0; + end; + + TitleScreen; + + InitializeGame; + + tsAddRoutine( @VBlankCounter, 1193180 div 240); + PlayMusic( gMenuSong); + if MainScreen then + repeat + ClearPlayerData( gPlayerData[0]); + ClearPlayerData( gPlayerData[1]); + MainLoop; + until not MainScreen; + + KillPics; + CleanUp; + + TextMode( oldMode); +end. + diff --git a/elements.pas b/elements.pas new file mode 100644 index 0000000..cef8067 --- /dev/null +++ b/elements.pas @@ -0,0 +1,218 @@ +unit Elements; + + +interface + + +const + + cIdHero1 = 0; + cIdHero2 = 1; + cIdEvil = 2; + + +type + + PCharacter = ^TCharacter; + TCharacter = + record + id : Byte; + + x, y : Integer; + xFrac, yFrac : Integer; + speed : Integer; + + direction : Byte; + faceDir : Byte; + facial : Byte; + face : Byte; + body : Byte; + gunPic : Byte; + gunPos : Byte; + phase : Byte; + command : Byte; + + armor : Integer; + lives : Integer; + invincibility : Integer; + dead : Boolean; + deathCount : Integer; + + gun : + record + kind : Byte; + lock : Byte; + ammo : Integer; + end; + closeCombat : Byte; + + cmd : Byte; + actionDelay, + delay : Byte; + + visible, + sleeping : Boolean; + moving, + shooting, + launching, + tracking : Byte; + isTarget : Boolean; + boobyTrapped : Boolean; + detouring : Boolean; + tryRight : Boolean; + turns : Integer; + currentDir : Word; + distanceToPlayer : Word; + + prev, + next : PCharacter; + end; + + PBullet = ^TBullet; + TBullet = + record + id : Byte; + kind : Byte; + x, y : Integer; + xFrac, + yFrac : Integer; + dx, dy : Integer; + range : Integer; + owner : Byte; + pic : Integer; + prev, + next : PBullet; + end; + + + PFireBall = ^TFireBall; + TFireBall = + record + stage : Integer; + x, y : Integer; + prev, + next : PFireBall; + end; + + +function AddCharacter( id : Byte ) : PCharacter; +function RemoveCharacter( c : PCharacter ) : PCharacter; +function AddBullet : PBullet; +function RemoveBullet( b : PBullet ) : PBullet; +function AddFireBall : PFireBall; +function RemoveFireBall( f : PFireBall ) : PFireBall; +procedure RemoveAllElements; + + +const + + gHero1 : PCharacter = nil; + gHero2 : PCharacter = nil; + gCharacters : PCharacter = nil; + gBullets : PBullet = nil; + gFireBalls : PFireBall = nil; + + + +implementation + + + +function AddCharacter( id : Byte ) : PCharacter; +var c : PCharacter; +begin + New( c); + FillChar( c^, SizeOf( c^), 0); + c^.id := id; + c^.prev := nil; + c^.next := gCharacters; + if gCharacters <> nil then + gCharacters^.prev := c; + gCharacters := c; + AddCharacter := c; +end; + + +function RemoveCharacter( c : PCharacter ) : PCharacter; +begin + if c^.next <> nil then + c^.next^.prev := c^.prev; + if c^.prev <> nil then + c^.prev^.next := c^.next + else + gCharacters := c^.next; + RemoveCharacter := c^.next; + if c = gHero1 then + gHero1 := nil; + if c = gHero2 then + gHero2 := nil; + Dispose( c); +end; + + +function AddBullet : PBullet; +var b : PBullet; +begin + New( b); + FillChar( b^, SizeOf( b^), 0); + b^.prev := nil; + b^.next := gBullets; + if gBullets <> nil then + gBullets^.prev := b; + gBullets := b; + AddBullet := b; +end; + + +function RemoveBullet( b : PBullet ) : PBullet; +begin + if b^.next <> nil then + b^.next^.prev := b^.prev; + if b^.prev <> nil then + b^.prev^.next := b^.next + else + gBullets := b^.next; + RemoveBullet := b^.next; + Dispose( b); +end; + + +function AddFireBall : PFireBall; +var f : PFireBall; +begin + New( f); + FillChar( f^, SizeOf( f^), 0); + f^.prev := nil; + f^.next := gFireBalls; + if gFireBalls <> nil then + gFireBalls^.prev := f; + gFireBalls := f; + AddFireBall := f; +end; + + +function RemoveFireBall( f : PFireBall ) : PFireBall; +begin + if f^.next <> nil then + f^.next^.prev := f^.prev; + if f^.prev <> nil then + f^.prev^.next := f^.next + else + gFireBalls := f^.next; + RemoveFireBall := f^.next; + Dispose( f); +end; + + +procedure RemoveAllElements; +begin + while gCharacters <> nil do + RemoveCharacter( gCharacters); + while gBullets <> nil do + RemoveBullet( gBullets); + while gFireBalls <> nil do + RemoveFireBall( gFireBalls); +end; + + +end. diff --git a/equip.pas b/equip.pas new file mode 100644 index 0000000..4f56a71 --- /dev/null +++ b/equip.pas @@ -0,0 +1,492 @@ +unit Equip; + + +interface + + +procedure GetEquipment( index : Integer; var cash : LongInt ); +function GetMenuCommand : Byte; + + +implementation + + uses SPX_Fnc, SPX_VGA, SPX_Txt, Pics, Fancy, BigFont, + Globals, Keyboard, Joystick, Stick, Screen, Sounds; + + +const + + cArmor = 255; + cAxe = 254; + cLife = 253; + cDone = 252; + + cSelectionMax = 9; + + +type + + EquipmentRec = + record + what : Byte; + pic : Integer; + description : String[80]; + price, ammoPrice, ammoBlock : Integer; + end; + + +function GetMenuCommand : Byte; +var command : Byte; + + procedure CheckStick( stick : Byte ); + begin + if stick = 0 then + Exit; + if StickLeft( stick) then + command := command or cCommandLeft; + if StickRight( stick) then + command := command or cCommandRight; + if StickUp( stick) then + command := command or cCommandUp; + if StickDown( stick) then + command := command or cCommandDown; + if StickButton1( stick) then + command := command or cCommandShoot; + if StickButton2( stick) then + command := command or cCommandSwitch; + end; + +begin + command := 0; + + VSinc; + PollSticks; + CheckStick( hero1Keys.stick); + CheckStick( hero2Keys.stick); + + if KeyDown( keyArrowLeft) then + command := command or cCommandLeft; + if KeyDown( keyArrowRight) then + command := command or cCommandRight; + if KeyDown( keyArrowUp) then + command := command or cCommandUp; + if KeyDown( keyArrowDown) then + command := command or cCommandDown; + if KeyDown( keyEnter) then + command := command or cCommandShoot; + if KeyDown( keySpace) then + command := command or cCommandShoot; + if KeyDown( keyBackspace) then + command := command or cCommandSwitch; + if KeyDown( keyKeypadMinus) then + command := command or cCommandSwitch; + if KeyDown( keyEsc) then + command := command or cCommandFreeze; + + GetMenuCommand := command; +end; + +procedure GetEquipment( index : Integer; var cash : LongInt ); +var choice : Integer; + done : Boolean; + selection : array [0..cSelectionMax] of EquipmentRec; + max : Integer; + +{ + function HasWeapon( kind : Byte; var which : Integer ) : Boolean; + var i : Integer; + begin + HasWeapon := True; + for i := 0 to cMaxWeaponry do + if gPlayerData[ index].weapons[i].kind = kind then + begin + which := i; + Exit; + end; + HasWeapon := False; + end; +} + + procedure MakeSelection; + + procedure SetEquipment( var eq : EquipmentRec; + what : Byte; + pic : Integer; + description : String; + price, + ammoPrice, + ammoBlock : Integer ); + begin + eq.what := what; + eq.pic := pic; + eq.description := description; + eq.price := price; + eq.ammoPrice := ammoPrice; + eq.ammoBlock := ammoBlock; + end; + + procedure SetWeapon( var eq : EquipmentRec; + what : Byte; + price, + ammoPrice, + ammoBlock : Integer ); + begin + SetEquipment( eq, what, + cGunStyles[ what].pic, cGunStyles[ what].name, + price, ammoPrice, ammoBlock); + end; + + + begin + SetEquipment( selection[0], cLife, cGoldSkullPic, 'Life', 4000, 0, 0); + SetEquipment( selection[1], cArmor, cArmorAddOnPic, 'Armor +', 2000, 0, 0); + SetEquipment( selection[2], cAxe, cAxePic, 'Chainsaw', 2000, 0, 0); + SetWeapon( selection[3], cBlaster, 1000, 500, 50); + SetWeapon( selection[4], cPowerGun, 2000, 300, 10); + SetWeapon( selection[5], cFlamer, 2500, 500, 100); + SetWeapon( selection[6], cSprayer, 2500, 500, 100); + SetWeapon( selection[7], cGrenade, 1500, 1000, 5); + SetWeapon( selection[8], cMegaGun, 5500, 3000, 100); + SetEquipment( selection[9], cDone, -1, 'Done', 0, 0, 0); + end; + + procedure DrawEquipScreen; + + procedure DrawEquipment( x, y : Integer; const eq : EquipmentRec ); + begin + {if eq.pic >= 0 then + fTPut( x, y, gPics[ eq.pic]^, False);} + if eq.ammoBlock <= 0 then + Inc( y, 5); + if eq.price > 0 then + DrawLetter( x, y + 4, cWhite, cBlack, eq.description + ', ' + St( eq.price + eq.ammoPrice) + 'Cr') + else + DrawLetter( x, y + 4, cWhite, cBlack, eq.description); + if eq.ammoBlock > 0 then + DrawLetter( x, y + 12, cGray, cBlack, 'Ammo: ' + St( eq.ammoPrice) + 'Cr/' + St( eq.ammoBlock)); + end; + + var i : Integer; + begin + FillScreen( cSteelPlate); + BigText( 40, 20, 'ARMOURY'); + + i := 0; + while i <= cSelectionMax do + begin + DrawEquipment( 105, 40 + (i div 2)*22, selection[i]); + Inc( i); + DrawEquipment( 215, 40 + (i div 2)*22, selection[i]); + Inc( i); + end; + + DrawLetter( 70, 180, cWhite, cBlack, 'Use up/down to highlight and Enter/button to buy'); + DrawLetter( 70, 190, cWhite, cBlack, 'Use Backspace/button #2 to sell'); + PCopy( 2, 1); + end; + + procedure DrawPlayer; + + procedure DrawInventory( x, y : Integer ); + var i : Integer; + begin + for i := 1 to gPlayerData[ index].lives do + fTPut( x + 20 + ((i-1) mod 2)*12, y - 10 + ((i-1) div 2)*12, gPics[ cGoldSkullPic]^, False); + DrawLetter( x + 7, y + 33, cWhite, cBlack, 'Armor:' + St( gPlayerData[ index].armor)); + if gPlayerData[ index].closeCombat > 1 then + DrawLetter( x + 7, y + 41, cWhite, cBlack, 'Chainsaw: ' + St( gPlayerData[ index].closeCombat div 2)) + else + DrawLetter( x + 7, y + 41, cWhite, cBlack, 'Barehanded'); + for i := 0 to cMaxWeaponry do + with gPlayerData[ index].weapons[i] do + if kind <> 0 then + begin + fTPut( x, y + 50 + i*20, gPics[ cGunStyles[ kind].pic]^, False); + DrawLetter( x + 20, y + 65 + i*20, cWhite, cBlack, St( ammo)); + end; + if gPlayerData[0].playing and gPlayerData[1].playing and + (g2PlayerPool and cPoolCash <> 0) then + DrawLetter( 10, 190, cRed, cBlack, 'Pool: '+St( cash) + 'Cr') + else + DrawLetter( 10, 190, cYellow, cBlack, 'Cash: '+St( cash) + 'Cr'); + end; + + var x, y : Integer; + begin + for x := 0 to 1 do + for y := 2 to 9 do + fPut( x*32, y*20, gPics[ cSteelPlate]^, False); + DrawInventory( 5, 40); + with cHeroInfo[ gPlayerData[ index].hero] do + begin + DrawCharacter( 10, 40, cDirectionDown, cDirectionDownRight, face, body, cGunDogs, cFaceNormal, 0, cGunIdle); + DrawLetter( 12, 65, cWhite, cBlack, name); + end; + CopyRect( 0, 25, 79, 199, pages[2]^, pages[1]^); + end; + + procedure DrawSelection; + + procedure DrawButton( x, y, index : Integer ); + begin + if choice = index then + fTPut( x, y, gPics[ cHilitedButton]^, False) + else + fTPut( x, y, gPics[ cButton]^, False); + end; + + var i : Integer; + begin + i := 0; + while i <= cSelectionMax do + begin + DrawButton( 85, 45 + (i div 2)*22, i); + Inc( i); + DrawButton( 195, 45 + (i div 2)*22, i); + Inc( i); + end; + + CopyRect( 80, 40, 319, 199, pages[2]^, pages[1]^); + end; + + function HasWeapon( kind : Byte ) : Boolean; + var i : Integer; + begin + HasWeapon := False; + for i := 0 to cMaxWeaponry do + if gPlayerData[ index].weapons[i].kind = kind then + HasWeapon := True; + end; + + function WeaponCount : Integer; + var i, count : Integer; + begin + count := 0; + for i := 0 to cMaxWeaponry do + if gPlayerData[ index].weapons[i].kind > 0 then + Inc( count); + WeaponCount := count; + end; + + function Cost( var eq : EquipmentRec ) : Integer; + begin + if (eq.ammoBlock > 0) and HasWeapon( eq.what) then + Cost := eq.ammoPrice + else + Cost := eq.price + eq.ammoPrice; + end; + + function CanBuy( var eq : EquipmentRec ) : Boolean; + var ok : Boolean; + begin + ok := True; + case eq.what of + + cLife: + ok := gPlayerData[ index].lives < 4; + + cArmor: + ok := gPlayerData[ index].armor < 50; + + cAxe: + ok := gPlayerData[ index].closeCombat <= 10; + + cPowerGun, cBlaster, cFlamer, cSprayer, cGrenade, cMegaGun: + ok := HasWeapon( eq.what) or (WeaponCount < 4); + + end; + CanBuy := ok and (Cost( eq) <= cash); + end; + + function AddWeapon( kind : Byte; ammo : Integer ) : Boolean; + var i : Integer; + begin + AddWeapon := True; + for i := 0 to cMaxWeaponry do + if gPlayerData[ index].weapons[i].kind = kind then + begin + Inc( gPlayerData[ index].weapons[i].ammo, ammo); + Exit; + end + else if gPlayerData[ index].weapons[i].kind <= 0 then + begin + gPlayerData[ index].weapons[i].kind := kind; + gPlayerData[ index].weapons[i].ammo := ammo; + Exit; + end; + AddWeapon := False; + end; + + procedure EnterPressed( choice : Integer ); + var i : Integer; + begin + if CanBuy( selection[ choice]) then + begin + Dec( cash, Cost( selection[ choice])); + PlaySound( cSwitchSound); + with selection[ choice] do + if what = cArmor then + Inc( gPlayerData[ index].armor, 5) + else if what = cAxe then + Inc( gPlayerData[ index].closeCombat, 2) + else if what = cLife then + Inc( gPlayerData[ index].lives) + else + AddWeapon( what, ammoBlock); + end + else + PlaySound( cScreamSound); + end; + + function CanSell( var eq : EquipmentRec ) : Boolean; + var ok : Boolean; + begin + ok := True; + case eq.what of + + cLife: + ok := gPlayerData[ index].lives > 1; + + cArmor: + ok := gPlayerData[ index].armor > 5; + + cAxe: + ok := gPlayerData[ index].closeCombat > 2; + + cPowerGun, cBlaster, cFlamer, cSprayer, cGrenade, cMegaGun: + ok := HasWeapon( eq.what); + + end; + CanSell := ok; + end; + + function RemoveWeapon( kind : Byte; ammo, price, ammoPrice : Integer ) : Boolean; + var i, j : Integer; + begin + RemoveWeapon := True; + for i := 0 to cMaxWeaponry do + if gPlayerData[ index].weapons[i].kind = kind then + begin + if gPlayerData[ index].weapons[i].ammo >= ammo then + begin + Dec( gPlayerData[ index].weapons[i].ammo, ammo); + Inc( cash, ammoPrice); + Exit; + end; + for j := i + 1 to cMaxWeaponry do + gPlayerData[ index].weapons[j-1] := gPlayerData[ index].weapons[j]; + gPlayerData[ index].weapons[ cMaxWeaponry].kind := 0; + Inc( cash, price); + end; + RemoveWeapon := False; + end; + + procedure MinusPressed( choice : Integer ); + var i : Integer; + begin + if CanSell( selection[ choice]) then + begin + PlaySound( cPickupSound); + with selection[ choice] do + begin + if what = cArmor then + Dec( gPlayerData[ index].armor, 5) + else if what = cAxe then + Dec( gPlayerData[ index].closeCombat, 2) + else if what = cLife then + Dec( gPlayerData[ index].lives) + else begin + RemoveWeapon( what, ammoBlock, price, ammoPrice); + Exit; + end; + Inc( cash, price); + end; + end + else + PlaySound( cScreamSound); + end; + +var cmd : Byte; +begin + if not gPlayerData[ index].playing then + Exit; + + choice := 0; + done := False; + + SetPageActive( 2); + MakeSelection; + DrawEquipScreen; + DrawPlayer; + DrawSelection; + FadeIn( 5, gPalette); + + while not done do + begin + if cmd <> GetMenuCommand then + begin + cmd := GetMenuCommand; + case cmd of + + cCommandShoot: + if selection[ choice].what = cDone then + begin + done := True; + PlaySound( cLaunchSound); + end + else begin + EnterPressed( choice); + DrawPlayer; + end; + + cCommandSwitch: + if selection[ choice].what <> cDone then + begin + MinusPressed( choice); + DrawPlayer; + end; + + cCommandUp: + begin + if choice > 1 then + Dec( choice, 2) + else + choice := cSelectionMax; + DrawSelection; + end; + + cCommandDown: + begin + if choice < cSelectionMax-1 then + Inc( choice, 2) + else + choice := 0; + DrawSelection; + end; + + cCommandLeft: + begin + if choice > 0 then + Dec( choice) + else + choice := cSelectionMax; + DrawSelection; + end; + + cCommandRight: + begin + if choice < cSelectionMax then + Inc( choice) + else + choice := 0; + DrawSelection; + end; + + end; + end; + end; + FadeOut( 5, gPalette); +end; + + +end. diff --git a/fancy.pas b/fancy.pas new file mode 100644 index 0000000..c8be8bd --- /dev/null +++ b/fancy.pas @@ -0,0 +1,101 @@ +unit Fancy; + + +interface + + uses SPX_Fnc, + {$IFDEF XLIB} XSPX {$ELSE} SPX_VGA {$ENDIF}; + + +procedure FancyPut( x, y : Integer; var pic; center : Boolean; dx, dy : Integer ); + + +implementation + + +type + + PByte = ^Byte; + + +procedure FancyPut( x, y : Integer; var pic; center : Boolean; dx, dy : Integer ); + +{$IFDEF XLIB} + +begin + fTPut_Clip( x, y, pic, center); +end; + +{$ELSE} + +var w, h : Integer; + xc, + x1, y1, x2, y2, + xOffset, + lines : Integer; + src, dst : PByte; + seg : Word; +begin + if dx < 0 then + dx := 0; + if dy < 0 then + dy := 0; + ImageDims( pic, w, h); + if not center then + begin + Inc( x, w div 2); + Inc( y, h div 2); + end; + Dec( x, (w div 2)*(1+dx)); + Dec( y, (h div 2)*(1+dy)); + + Dec( h); + if y < WinMinY then + begin + y1 := 1 + (WinMinY - y - 1) div (1+dy); + y := y + y1*dy; + end + else + y1 := 0; + if y + h*dy > WinMaxY then + y2 := 1 + (h*dy - WinMaxY - 1) div (1+dy) + else + y2 := h; + + if x < WinMinX then + begin + x1 := 1 + (WinMinX - x - 1) div (1+dx); + x := x + x1*dx; + end + else + x1 := 0; + if x + (w-1)*dx > WinMaxX then + x2 := 1 + ((w-1)*dx - WinMaxX - 1) div (1+dx) + else + x2 := w; + + xOffset := x1 + (w - x2); + lines := y2 - y1; + src := PByte( @pic); + Inc( LongInt( src), 4 + y1*w + x1); + dst := Ptr( ScnSeg, Pt( x, y)); + while lines >= 0 do + begin + dst := Ptr( ScnSeg, Pt( x, y)); + for xc := x1 to x2-1 do + begin + if src^ <> 0 then + dst^ := src^; + Inc( src); + Inc( LongInt( dst), 1+dx); + end; + Inc( LongInt( src), xOffset); + Inc( y, 1+dy); + Dec( lines); + end; +end; + +{$ENDIF} + + +end. diff --git a/game.pas b/game.pas new file mode 100644 index 0000000..ca4d803 --- /dev/null +++ b/game.pas @@ -0,0 +1,1402 @@ +unit Game; + + +interface + + uses Globals, Elements; + + +procedure CheckIfGameLost; +procedure GetPlayerCommands( fastPhase : Boolean ); +procedure MovePlayer( cmd : Integer; c, c2 : PCharacter; fastPhase : Boolean ); +procedure MoveBadGuys( fastPhase : Boolean ); +procedure MoveBullets( fastPhase : Boolean ); +procedure MoveAllCharacters; +procedure UpdateCharacters( fastPhase, slowPhase : Boolean ); +procedure UpdateExplosions( fastPhase : Boolean ); +function MakeBadGuy( baddieType : Byte; makeTarget : Boolean ) : Boolean; +function MakeAnyBadGuy : Boolean; +procedure RelocateBadGuys; +procedure ClearAllPosFlags; +procedure SetPosFlag( c : PCharacter ); + + +implementation + + uses Keyboard, Joystick, Stick, Pics, GameArea, Buffer, Screen, Sounds; + + + +procedure ClearAllPosFlags; +begin + FillChar( gCharacterMap^, SizeOf( gCharacterMap^), 0); +end; + + +function PotentialCollision( x, y : Integer ) : Boolean; +var f : Word; + x1, x2, y1, y2 : Integer; +begin + PotentialCollision := False; + x1 := x div cwCharMap; + x2 := (x + cCharacterWidth) div cwCharMap; + y1 := y div chCharMap; + y2 := (y + cCharacterHeight) div chCharMap; + if (x1 < cxFlagMin) or (x2 > cxFlagMax) or + (y1 < cyMin) or (y2 > cyMax) then + Exit; + f := gCharacterMap^[ x1, y1]; + f := f + gCharacterMap^[ x1, y2]; + f := f + gCharacterMap^[ x2, y1]; + f := f + gCharacterMap^[ x2, y2]; + PotentialCollision := f > 4; +end; + + +function PotentialHit( x, y, radius : Integer ) : Boolean; +var f : Word; + x1, x2, y1, y2 : Integer; +begin + x1 := (x - radius) div cwCharMap; + x2 := (x + radius) div cwCharMap; + y1 := (y - radius) div chCharMap; + y2 := (y + radius) div chCharMap; + f := gCharacterMap^[ x1, y1]; + f := f + gCharacterMap^[ x1, y2]; + f := f + gCharacterMap^[ x2, y1]; + f := f + gCharacterMap^[ x2, y2]; + PotentialHit := f > 0; +end; + + +procedure ClearPosFlag( c : PCharacter ); +var x1, x2, y1, y2 : Integer; +begin + x1 := c^.x div cwCharMap; + x2 := (c^.x + cCharacterWidth) div cwCharMap; + y1 := c^.y div chCharMap; + y2 := (c^.y + cCharacterHeight) div chCharMap; + Dec( gCharacterMap^[ x1, y1]); + Dec( gCharacterMap^[ x1, y2]); + Dec( gCharacterMap^[ x2, y1]); + Dec( gCharacterMap^[ x2, y2]); +end; + + +procedure SetPosFlag( c : PCharacter ); +var x1, x2, y1, y2 : Integer; +begin + x1 := c^.x div cwCharMap; + x2 := (c^.x + cCharacterWidth) div cwCharMap; + y1 := c^.y div chCharMap; + y2 := (c^.y + cCharacterHeight) div chCharMap; + Inc( gCharacterMap^[ x1, y1]); + Inc( gCharacterMap^[ x1, y2]); + Inc( gCharacterMap^[ x2, y1]); + Inc( gCharacterMap^[ x2, y2]); +end; + + +procedure KillCharacter( c : PCharacter; byChainSaw : Boolean ); +var x, y : Integer; +begin + if byChainSaw then + DoSound( cChainSawSound) + else + DoSound( cScreamSound); + if c^.isTarget then + Dec( gTargetsLeft); + + x := (c^.x + cCharacterWidth div 2) div 32; + y := (c^.y + 12) div 24; + if gWorld[ x, y] = cBkgFloor then + begin + if c^.id < cIdEvil then + gWorld[ x, y] := cBkgFloorSkull + else + gWorld[ x, y] := cBkgFloorBlood; + end; + ClearPosFlag( c); + c^.armor := 0; + c^.dead := True; + c^.deathCount := 0; +end; + + +function HurtCharacter( c : PCharacter; attackerId : Byte; damage : Integer; closeCombat : Boolean ) : Boolean; +begin + if (damage > 0) and (c^.invincibility = 0) then + begin + if attackerId < cIdEvil then + begin + if damage < c^.armor then + Inc( gPlayerData[ attackerId].mission.score, damage) + else + Inc( gPlayerData[ attackerId].mission.score, c^.armor); + end; + Dec( c^.armor, damage); + if c^.armor <= 0 then + begin + KillCharacter( c, closeCombat and (attackerId <> cIdEvil) and (damage > 1)); + if attackerId < cIdEvil then + begin + Inc( gPlayerData[ attackerId].mission.kills); + if closeCombat then + Inc( gPlayerData[ attackerId].mission.closeCombat); + end; + end; + end; +end; + + +function CloseContact( c : PCharacter; x, y : LongInt ) : Boolean; +var c2 : PCharacter; +begin + CloseContact := False; + + if PotentialCollision( x, y) then + begin + c2 := gCharacters; + while c2 <> nil do + begin + if not c2^.dead and (c <> c2) and + (Abs( x - c2^.x) <= cCharacterWidth) and + (Abs( y - c2^.y) <= cCharacterHeight) then + begin + {if (c2^.id <> cIdEvil) and (c^.id = cIdEvil) and + (c2^.invincibility > 0) then + KillCharacter( c, False) + else} + if (c2^.invincibility <= 0) and + ((c2^.id = cIdEvil) xor (c^.id = cIdEvil)) then + HurtCharacter( c2, c^.id, c^.closeCombat, True); + if (Abs( x - c2^.x) < Abs( c^.x - c2^.x)) or + (Abs( y - c2^.y) < Abs( c^.y - c2^.y)) then + CloseContact := True; + Exit; + end; + c2 := c2^.next; + end; + end; +end; + + +function PositionOK( x, y : LongInt ) : Boolean; +{ + function WorldBlocked( x, y, xMod : Integer ) : Boolean; + var w : Byte; + begin + w := gWorld[ x, y]; + if w and cNoWalk <> 0 then + WorldBlocked := True + else if (w and cNoWalkLeft <> 0) and (xMod < 16) then + WorldBlocked := True + else + WorldBlocked := False; + end; +} +var x1, y1, + x2, y2 : Integer; +begin + PositionOk := False; + + Inc( y, 10); + x1 := x div cwWalkMap; + y1 := y div chWalkMap; + x2 := (x + cCharacterWidth) div cwWalkMap; + y2 := (y + 12) div chWalkMap; + + if (x1 < cxFlagMin) or (x2 > cxFlagMax) or + (y1 < cyMin) or (y2 > cyMax) then + Exit; + if gNoWalk^[ x1, y1] or + gNoWalk^[ x1, y2] or + gNoWalk^[ x2, y1] or + gNoWalk^[ x2, y2] then + Exit; + + PositionOk := True; +end; + + +function CheckPosition( c : PCharacter; dx, dy : Integer ) : Boolean; +var nx, ny : LongInt; +begin + CheckPosition := True; + nx := c^.x*8 + c^.xFrac + dx * c^.speed; + ny := c^.y*8 + c^.yFrac + dy * c^.speed; + + if CloseContact( c, nx div 8, ny div 8) then + Exit; + + if (dx <> 0) and (dy <> 0) and PositionOK( nx div 8, ny div 8) then + begin + ClearPosFlag( c); + c^.x := nx div 8; + c^.xFrac := nx and 7; + c^.y := ny div 8; + c^.yFrac := ny and 7; + SetPosFlag( c); + end + else if (dx <> 0) and PositionOK( nx div 8, c^.y) then + begin + ClearPosFlag( c); + c^.x := nx div 8; + c^.xFrac := nx and 7; + SetPosFlag( c); + end + else if (dy <> 0) and PositionOK( c^.x, ny div 8) then + begin + ClearPosFlag( c); + c^.y := ny div 8; + c^.yFrac := ny and 7; + SetPosFlag( c); + end + else + CheckPosition := False; +end; + + +function MoveCharacter( c : PCharacter; cmd : Byte; slowPhase : Boolean ) : Boolean; +var dx, dy : Integer; +begin + MoveCharacter := False; + dx := 0; + dy := 0; + + if cmd and cCommandMoving <> 0 then + begin + if gFreeMovement or + (cmd and cCommandFiring = 0) or + (cmd and cCommandNoTurn <> 0) then + begin + if cmd and cCommandLeft <> 0 then + dx := -1 + else if cmd and cCommandRight <> 0 then + dx := 1; + if cmd and cCommandUp <> 0 then + dy := -1 + else if cmd and cCommandDown <> 0 then + dy := 1; + MoveCharacter := CheckPosition( c, dx, dy); + + if slowPhase then + c^.phase := 1 + (c^.phase and 3); + end; + + if cmd and cCommandNoTurn = 0 then + c^.direction := cDirectionFromCmd[ cmd and cCommandMoving]; + c^.faceDir := c^.direction; + end + else if slowPhase then + c^.phase := 0; +end; + + +procedure CheckIfGameLost; +begin + if ((gHero1 = nil) or (gHero1^.dead)) and + ((gHero2 = nil) or (gHero2^.dead)) then + gGameOver := True; +end; + + +procedure PickPos( var x, y : Integer ); +begin + if (gHero1 <> nil) and (gHero2 <> nil) then + begin + if Random( 2) = 0 then + begin + x := gHero1^.x; + y := gHero1^.y; + end + else begin + x := gHero2^.x; + y := gHero2^.y; + end + end + else if gHero1 <> nil then + begin + x := gHero1^.x; + y := gHero1^.y; + end + else if gHero2 <> nil then + begin + x := gHero2^.x; + y := gHero2^.y; + end; + Inc( x, Random( 2*cRelocationDistance)); + Inc( y, Random( 2*cRelocationDistance)); + Dec( x, cRelocationDistance); + Dec( y, cRelocationDistance); +end; + + +function MakeBadGuy( baddieType : Byte; makeTarget : Boolean ) : Boolean; +var c : PCharacter; + + function PlacementOk( c : PCharacter ) : Boolean; + begin + PlacementOk := False; + if not PositionOK( c^.x, c^.y) then + Exit; + SetPosFlag( c); + if CloseContact( c, c^.x, c^.y) or + CharacterVisible( c) then + begin + ClearPosFlag( c); + Exit; + end; + PlacementOk := True; + end; + +begin + c := AddCharacter( cIdEvil); + c^.direction := 4; + c^.faceDir := c^.direction; + c^.gunPos := cGunReady; + PickPos( c^.x, c^.y); + c^.gun.kind := cGoonGun; + c^.gun.ammo := 10000; + c^.gunPic := cGunDogs; + c^.isTarget := makeTarget; + if not c^.isTarget and (Random( 10) = 0) then + c^.sleeping := False + else + c^.sleeping := True; + + if PlacementOk( c) then + begin + c^.gun.lock := 120; + + case baddieType of + + cBaddieKiller: + begin + c^.body := cRedBody; + c^.face := cFaceGrunt2; + c^.tracking := 15; + c^.moving := 15; + c^.shooting := 2; + c^.actionDelay := 20; + c^.speed := 12; + c^.armor := 10; + c^.closeCombat := 1; + c^.gun.kind := cGoonGun2; + end; + + cBaddieRobot: + begin + c^.body := cGreyBody; + c^.face := cFaceMechGrunt; + c^.tracking := 15; + c^.moving := 15; + c^.shooting := 5; + c^.actionDelay := 25; + c^.speed := 4; + c^.armor := 20; + c^.closeCombat := 1; + c^.gun.kind := cGoonGun3; + end; + + cBaddieLittleBad: + begin + c^.body := cBlackBody; + c^.face := cFaceGrunt2; + c^.tracking := 15; + c^.moving := 15; + c^.shooting := 1; + c^.actionDelay := 10; + c^.speed := 16; + c^.armor := 10; + c^.closeCombat := 2; + c^.gun.kind := cGoonGun2; + end; + + cBaddieMeanGoon: + begin + c^.body := cBrownBody; + c^.face := cFaceBlondGrunt; + c^.tracking := 15; + c^.moving := 15; + c^.shooting := 5; + c^.actionDelay := 15; + c^.speed := 8; + c^.armor := 15; + c^.closeCombat := 1; + c^.gun.kind := cGoonGun2; + end; + + cBaddieBigBad: + begin + c^.body := cBlackBody; + c^.face := cFaceBigBadGuy; + c^.tracking := 10; + c^.moving := 10; + c^.shooting := 5; + c^.actionDelay := 10; + c^.speed := 8; + c^.armor := 20; + c^.closeCombat := 3; + c^.gun.kind := cGoonGun4; + end; + + else + begin + c^.body := cBrownBody; + c^.face := cFaceGrunt; + c^.tracking := 10; + c^.moving := 10; + c^.shooting := 2; + c^.actionDelay := 25; + c^.speed := 6; + c^.armor := 15; + end; + end; + + c^.invincibility := 30; + + MakeBadGuy := True; + end + else begin + RemoveCharacter( c); + MakeBadGuy := False; + end; +end; + + +function MakeAnyBadGuy : Boolean; +var x : Integer; + kind : Integer; +begin + x := Random( 100); + for kind := cBaddieGoon to cBaddieBigBad do + begin + if x < gBaddieProbs[ kind] then + begin + MakeAnyBadGuy := MakeBadGuy( kind, False); + Exit; + end; + Dec( x, gBaddieProbs[ kind]); + end; + MakeAnyBadGuy := MakeBadGuy( cBaddieGoon, False); +end; + + +procedure Explode( cx, cy : LongInt; bigBang : Boolean ); +var f : PFireBall; + count, + delay : Integer; +begin + if bigBang then + DoSound( cBangSound) + else + DoSound( cSmallBangSound); + for count := 1 to cFireBallsPerExplosion do + begin + f := AddFireBall; + with f^ do + begin + stage := 1 - count; + x := cx - 16 + Random(32); + y := cy - 16 + Random(32); + end; + end; + cx := cx div cwWorldMap; + cy := cy div chWorldMap; + if gWorld[ cx, cy] = cBkgFloor then + gWorld[ cx, cy] := cBkgFloorCrater; +end; + + +procedure UpdateCharacters( fastPhase, slowPhase : Boolean ); + + function UpdateCharacter( c : PCharacter ) : Boolean; + begin + UpdateCharacter := True; + with c^ do + begin + if gun.lock > 0 then + begin + if gun.lock > 1{gCyclesPerFrame} then + Dec( gun.lock) + else + gun.lock := 0; + if (gunPos = cGunRecoil) and (gun.lock + 5 < cGunStyles[ gun.kind].rate) then + gunPos := cGunReady; + end + else begin + facial := cFaceNormal; + if id <> cIdEvil then + gunPos := cGunIdle; + end; + + if invincibility <> 0 then + Dec( invincibility); + + if slowPhase and (c^.phase = 0) and (c^.gunPos = cGunIdle) and (Random( 6) = 0) then + faceDir := (direction + Random( 3) + 7) and 7; + + if not dead then + Exit; + + if not fastPhase then + Exit; + + Inc( deathCount); + if deathCount > cMaxDeath then + begin + if id = cIdEvil then + UpdateCharacter := False + else begin + if lives > 1 then + begin + Dec( lives); + armor := gPlayerData[ id].armor; + dead := False; + SetPosFlag( c); + invincibility := 240; + end + else if (g2PlayerPool and cPoolLives <> 0) and + (gHero1 <> nil) and (gHero2 <> nil) and + ((gHero1^.lives > 1) or (gHero2^.lives > 1)) then + begin + if c = gHero1 then + Dec( gHero2^.lives) + else + Dec( gHero1^.lives); + armor := gPlayerData[ id].armor; + dead := False; + SetPosFlag( c); + invincibility := 240; + end + else begin + UpdateCharacter := False; + CheckIfGameLost; + end; + end; + end; + end; + end; + +var c : PCharacter; +begin + c := gCharacters; + while c <> nil do + begin + if UpdateCharacter( c) then + c := c^.next + else + c := RemoveCharacter( c); + end; +end; + + +function HitWall( b : PBullet ) : Boolean; +var x, y1, y2 : Integer; + flag : Byte; +begin + x := b^.x div cwWalkMap; + y1 := b^.y div chWalkMap; + y2 := (b^.y + 10) div chWalkMap; + + HitWall := gNoWalk^[ x, y1] and gNoWalk^[ x, y2]; +end; + + +procedure BlastCharacters( b : PBullet ); +var c : PCharacter; + radius : Integer; +begin + b^.range := 0; + Explode( b^.x, b^.y, True); + c := gCharacters; + while c <> nil do + begin + with c^ do + if (invincibility <= 0) and not dead and + ((id = cIdEvil) xor (b^.id = cIdEvil)) and + (armor > 0) and + (x + 8 > b^.x - 100) and (x + 8 < b^.x + 100) and + (y + 10 > b^.y - 100) and (y + 10 < b^.y + 100) then + HurtCharacter( c, b^.id, cGunStyles[ b^.kind].power, False); + c := c^.next; + end; +end; + + +function HitCharacter( b : PBullet ) : Boolean; +var c : PCharacter; + r : Integer; +begin + r := cGunStyles[ b^.kind].radius; + if PotentialHit( b^.x, b^.y, r) then + begin + c := gCharacters; + while c <> nil do + begin + with c^ do + if ((b^.id = cIdEvil) xor (id = cIdEvil)) and + (not dead) and + (b^.y + r > y) and (b^.y - r < y + cCharacterHeight) and + (b^.x + r > x) and (b^.x - r < x + cCharacterWidth) then + begin + if b^.kind <> cGrenade then + begin + {DoSound( cBulletHitSound);} + if invincibility <= 0 then + HurtCharacter( c, b^.id, cGunStyles[ b^.kind].power, False); + if b^.id <> cIdEvil then + Inc( gPlayerData[ b^.id].mission.hits) + else if c^.id <> cIdEvil then + Inc( gPlayerData[ c^.id].mission.hitsTaken); + end + else + BlastCharacters( b); + HitCharacter := True; + Exit; + end; + c := c^.next; + end; + end; + HitCharacter := False; +end; + + +procedure CheckForStructureDamage( b : PBullet ); + + procedure TargetHit( x, y : Integer ); + begin + Dec( gTargetsLeft); + {gWorld[ x, y] := cBkgFloorCrater; + gNoWalk^[ 2*x, y] := False; + gNoWalk^[ 2*x + 1, y] := False;} + if b^.id < cIdEvil then + begin + Inc( gPlayerData[ b^.id].mission.targets); + Inc( gPlayerData[ b^.id].mission.score, 50); + end; + end; + + function CheckStructure( x, y : Integer ) : Boolean; + begin + CheckStructure := False; + with gStructureMap^[ x, y] do + begin + if structure = 0 then + Exit; + CheckStructure := True; + if cGunStyles[ b^.kind].power >= structure then + begin + if b^.id < cIdEvil then + Inc( gPlayerData[ b^.id].mission.demolition, structure); + structure := 0; + Explode( x*cwWorldMap + cwWorldMap div 2, y*chWorldMap + chWorldMap div 2, gWorld[ x, y] in gMissionTargets); + if gWorld[ x, y] in gMissionTargets then + TargetHit( x, y); + gWorld[ x, y] := wreckagePic; + gNoWalk^[ 2*x, y] := (wreckagePic and (cNoWalk or cNoWalkLeft) <> 0); + gNoWalk^[ 2*x + 1, y] := (wreckagePic and cNoWalk <> 0); + end + else begin + Dec( structure, cGunStyles[ b^.kind].power); + if b^.id < cIdEvil then + Inc( gPlayerData[ b^.id].mission.demolition, cGunStyles[ b^.kind].power); + end; + end; + + end; + +var x, y1, y2 : Integer; +begin + {if b^.id = cIdEvil then + Exit;} + + x := b^.x div cwWorldMap; + y1 := b^.y div chWorldMap; + y2 := (b^.y + 10) div chWorldMap; + if not CheckStructure( x, y1) then + CheckStructure( x, y2); +end; + + +procedure MoveBullets( fastPhase : Boolean ); +var b : PBullet; + + procedure ShotConnected; + begin + with b^ do + begin + Inc( x, Random( 4)); + Dec( x, 2); + Inc( y, Random( 4)); + Dec( y, 2); + pic := cShotConnectPic; + range := -3; + end; + end; + +begin + b := gBullets; + while b <> nil do + begin + with b^ do + begin + if range > 0 then + begin + x := x*8 + xFrac; + y := y*8 + yFrac; + Inc( x, dx); + Inc( y, dy); + xFrac := x and 7; + yFrac := y and 7; + x := x div 8; + y := y div 8; + Dec( range); + if range > 0 then + begin + if HitWall( b) then + begin + CheckForStructureDamage( b); + if kind = cGrenade then + BlastCharacters( b) + else + ShotConnected; + end + else if HitCharacter( b) then + ShotConnected + else if fastPhase and (cGunStyles[ kind].animated <> cAnimationNone) then + pic := (pic + 1) and 7; + end + else if kind = cGrenade then + BlastCharacters( b); + b := b^.next; + end + else begin + Inc( range); + if range >= 0 then + b := RemoveBullet( b) + else + b := b^.next; + end; + end; + end; +end; + + +procedure UpdateExplosions( fastPhase : Boolean ); +var f : PFireBall; +begin + if not fastPhase then + Exit; + + f := gFireBalls; + while f <> nil do + with f^ do + begin + Inc( stage); + if stage > cMaxFireBalls + cFireBallsPerExplosion then + f := RemoveFireBall( f) + else + f := f^.next; + end; +end; + + +procedure Shoot( c : PCharacter ); +var b : PBullet; +begin + if (c^.gun.ammo = 0) or (c^.gun.lock <> 0) then + Exit; + + b := AddBullet; + with b^ do + begin + kind := c^.gun.kind; + id := c^.id; + if (c^.id <> cIdEvil) and (kind <> cGrenade) then + Inc( gPlayerData[ c^.id].mission.shotsFired); + DoSound( cGunStyles[ kind].sound); + c^.gun.lock := cGunStyles[ kind].rate; + c^.gunPos := cGunRecoil; + c^.facial := cFaceHard; + c^.faceDir := c^.direction; + Dec( c^.gun.ammo); + pic := c^.direction; + range := cGunStyles[ kind].range; + + x := c^.x + cMuzzleOfs[ c^.direction].x; + y := c^.y + cMuzzleOfs[ c^.direction].y; + dx := cMuzzleDXY[ c^.direction].x * cGunStyles[ kind].speed; + dy := cMuzzleDXY[ c^.direction].y * cGunStyles[ kind].speed; + xFrac := 0; + yFrac := 0; + end; +end; + + +function LeavingOtherPlayer( cmd : Integer; c1, c2 : PCharacter ) : Boolean; +var x, y : LongInt; +begin + if (gSplitScreenMode <> cSplitScreenNever) or + (c1 = nil) or (c2 = nil) or + c1^.dead or c2^.dead then + begin + LeavingOtherPlayer := False; + Exit; + end; + LeavingOtherPlayer := True; + + x := c1^.x; + y := c1^.y; + + if cmd and cCommandLeft <> 0 then + begin + Dec( x); + if c2^.x - x > cMaxHorizontalDistance then + Exit; + end + else if cmd and cCommandRight <> 0 then + begin + Inc( x); + if x - c2^.x > cMaxHorizontalDistance then + Exit; + end; + if cmd and cCommandUp <> 0 then + begin + Dec( y); + if c2^.y - y > cMaxVerticalDistance then + Exit; + end + else if cmd and cCommandDown <> 0 then + begin + Inc( y); + if y - c2^.y > cMaxVerticalDistance then + Exit; + end; + LeavingOtherPlayer := False; +end; + + +procedure CheckIfPickup( c : PCharacter ); + + function AddWeapon( kind : Byte; ammo : Integer ) : Boolean; + var i : Integer; + begin + AddWeapon := True; + for i := 0 to cMaxWeaponry do + if (gPlayerData[ c^.id].weapons[i].kind = kind) or + (gPlayerData[ c^.id].weapons[i].kind <= 0) then + begin + gPlayerData[ c^.id].weapons[i].kind := kind; + Inc( gPlayerData[ c^.id].weapons[i].ammo, ammo); + if kind = c^.gun.kind then + c^.gun.ammo := gPlayerData[ c^.id].weapons[i].ammo; + Exit; + end; + AddWeapon := False; + end; + +var x, y : Integer; +begin + x := c^.x div cwWorldMap; + y := (c^.y + 10) div chWorldMap; + case gWorld[ x, y] of + cBkgFloorItem1: if not AddWeapon( cPowerGun, 5) then Exit; + cBkgFloorItem2: if not AddWeapon( cBlaster, 50) then Exit; + cBkgFloorItem3: if not AddWeapon( cSprayer, 100) then Exit; + cBkgFloorItem4: if not AddWeapon( cFlamer, 50) then Exit; + cBkgFloorItem5: if not AddWeapon( cGrenade, 5) then Exit; + cBkgFloorItem6: if c^.armor < gPlayerData[ c^.id].armor then + c^.armor := gPlayerData[ c^.id].armor + else + Exit; + cBkgFloorItem7: Inc( c^.lives); + cBkgFloorArmorAddOn: + if c^.armor < 70 then + c^.armor := 70 + else + Exit; + cBkgFloorAxe: Inc( c^.closeCombat, 10); + cBkgFloorDocs, + cBkgFloorFolder, + cBkgFloorDisk, + cBkgFloorCircuit, + cBkgFloorTeddy: + Inc( gPlayerData[ c^.id].mission.objects); + + else + Exit; + end; + DoSound( cPickupSound); + gWorld[ x, y] := cBkgFloor; +end; + + +procedure CheckIfExit( c : PCharacter ); +var x, y, i : Integer; +begin + x := c^.x div cwWorldMap; + y := (c^.y + 10) div chWorldMap; + if {(gTargetsLeft <= 0) and} (gWorld[ x, y] = cBkgExit) then + begin + if c^.gun.kind > 0 then + begin + i := 0; + while gPlayerData[ c^.id].weapons[ i].kind <> c^.gun.kind do + Inc( i); + gPlayerData[ c^.id].weapons[ i].ammo := c^.gun.ammo; + end; + gPlayerData[ c^.id].completed := True; + {if c^.lives < gPlayerData[ c^.id].lives then} + gPlayerData[ c^.id].lives := c^.lives; + gPlayerData[ c^.id].mission.time := gMissionTime div 60; + RemoveCharacter( c); + if (gHero1 = nil) and (gHero2 = nil) then + gGameOver := True; + end; +end; + + +procedure MovePlayer( cmd : Integer; c, c2 : PCharacter; fastPhase : Boolean ); +begin + if (c = nil) or c^.dead or LeavingOtherPlayer( cmd, c, c2) then + Exit; + + c^.command := cmd; + MoveCharacter( c, cmd, fastPhase); + CheckIfPickup( c); + CheckIfExit( c); + + if cmd and cCommandShoot <> 0 then + Shoot( c); +end; + + +function GetStickCommand( const keys : KeyControls; c : PCharacter ) : Byte; +var cmd : Byte; +begin + cmd := 0; + if c = nil then + Exit; + + if StickUp( keys.stick) then + cmd := cmd or cCommandUp + else if StickDown( keys.stick) then + cmd := cmd or cCommandDown; + if StickLeft( keys.stick) then + cmd := cmd or cCommandLeft + else if StickRight( keys.stick) then + cmd := cmd or cCommandRight; + + if StickButton1( keys.stick) then + cmd := cmd or cCommandShoot; + if StickButton2( keys.stick) then + cmd := cmd or cCommandSwitch; + + GetStickCommand := cmd; +end; + + +function GetKeybCommand( const keys : KeyControls; c : PCharacter ) : Byte; +var cmd : Byte; +begin + cmd := 0; + if c = nil then + Exit; + + { + if c^.delay > 0 then + Dec( c^.delay); + if keys.rotate then + begin + if KeyDown( keys.left) and (c^.delay = 0) then + begin + c^.direction := (c^.direction + 7) and 7; + c^.delay := 6; + end + else if KeyDown( keys.right) and (c^.delay = 0) then + begin + c^.direction := (c^.direction + 1) and 7; + c^.delay := 6; + end; + if KeyDown( keys.up) then + cmd := cCmdFromDirection[ c^.direction] + else if KeyDown( keys.down) then + cmd := cCmdFromDirection[ (c^.direction + 4) and 7] or cCommandNoTurn; + end + } + if KeyDown( keys.up) then + cmd := cmd or cCommandUp + else if KeyDown( keys.down) then + cmd := cmd or cCommandDown; + if KeyDown( keys.left) then + cmd := cmd or cCommandLeft + else if KeyDown( keys.right) then + cmd := cmd or cCommandRight; + + if KeyDown( keys.shoot) then + cmd := cmd or cCommandShoot; + if KeyDown( keys.switch) then + cmd := cmd or cCommandSwitch; + + GetKeybCommand := cmd; +end; + + +procedure SwitchWeapon( index : Integer; c : PCharacter ); +var current, next : Integer; +begin + current := 0; + while gPlayerData[ index].weapons[ current].kind <> c^.gun.kind do + Inc( current); + next := current; + repeat + Inc( next); + if (next > cMaxWeaponry) then + next := 0; + until (next = current) or + ((gPlayerData[ index].weapons[ next].kind <> 0) and + (gPlayerData[ index].weapons[ next].ammo > 0)); + + if next <> current then + begin + DoSound( cSwitchSound); + gPlayerData[ index].weapons[ current].ammo := c^.gun.ammo; + c^.gun.kind := gPlayerData[ index].weapons[ next].kind; + c^.gun.ammo := gPlayerData[ index].weapons[ next].ammo; + c^.gun.lock := 0; + end; +end; + + +procedure AdjustCommand( index : Integer; var command : Byte ); +begin + with gPlayerData[ index] do + begin + if command and cCommandSwitch <> 0 then + begin + button2Down := True; + if command and cCommandMoving <> 0 then + begin + command := command or cCommandNoTurn; + button2Movement := True; + end; + command := command and not cCommandSwitch; + end + else begin + if button2Down and not button2Movement then + command := command or cCommandSwitch; + button2Down := False; + button2Movement := False; + end; + end; +end; + + +procedure GetPlayerCommands( fastPhase : Boolean ); +var command : Byte; +begin + PollSticks; + if hero1Keys.stick > 0 then + command := GetStickCommand( Hero1Keys, gHero1) + else + command := GetKeybCommand( Hero1Keys, gHero1); + AdjustCommand( 0, command); + + if command and cCommandSwitch <> 0 then + SwitchWeapon( 0, gHero1); + MovePlayer( command, gHero1, gHero2, fastPhase); + + if hero2Keys.stick > 0 then + command := GetStickCommand( Hero2Keys, gHero2) + else + command := GetKeybCommand( Hero2Keys, gHero2); + AdjustCommand( 1, command); + + if command and cCommandSwitch <> 0 then + SwitchWeapon( 1, gHero2); + MovePlayer( command, gHero2, gHero1, fastPhase); +end; + + +function Facing( c, c2 : PCharacter ) : Boolean; +begin + case c^.direction of + 0: Facing := (c^.y > c2^.y); + 1: Facing := (c^.y > c2^.y) and (c^.x < c2^.x); + 2: Facing := (c^.x < c2^.x); + 3: Facing := (c^.y < c2^.y) and (c^.x < c2^.x); + 4: Facing := (c^.y < c2^.y); + 5: Facing := (c^.y < c2^.y) and (c^.x > c2^.x); + 6: Facing := (c^.x > c2^.x); + 7: Facing := (c^.y > c2^.y) and (c^.x > c2^.x); + else Facing := False; + end; +end; + + +function FacingPlayer( c : PCharacter ) : Boolean; +var c2 : PCharacter; +begin + FacingPlayer := True; + + if (gHero1 <> nil) and (not gHero1^.dead) and Facing( c, gHero1) then + Exit; + if (gHero2 <> nil) and (not gHero2^.dead) and Facing( c, gHero2) then + Exit; + + FacingPlayer := False; +end; + + +procedure GetTargetCoords( c : PCharacter; var x, y : LongInt ); +begin + if (gHero1 <> nil) and not gHero1^.dead and + (gHero2 <> nil) and not gHero2^.dead then + begin + if Distance( c, gHero1) < Distance( c, gHero2) then + begin + x := gHero1^.x; + y := gHero1^.y; + end + else begin + x := gHero2^.x; + y := gHero2^.y; + end; + end + else if (gHero1 <> nil) and not gHero1^.dead then + begin + x := gHero1^.x; + y := gHero1^.y; + end + else if (gHero2 <> nil) and not gHero2^.dead then + begin + x := gHero2^.x; + y := gHero2^.y; + end + else begin + x := c^.x; + y := c^.y; + end; +end; + + +function Hunt( c : PCharacter ) : Byte; +var cmd : Byte; + x, y, dx, dy : LongInt; +begin + cmd := 0; + + GetTargetCoords( c, x, y); + {x := gHorzPos + 150; + y := gVertPos + 90;} + + dx := Abs( x - c^.x); + dy := Abs( y - c^.y); + + if 2*dx > dy then + begin + if c^.x < x then + cmd := cmd or cCommandRight + else if c^.x > x then + cmd := cmd or cCommandLeft; + end; + if 2*dy > dx then + begin + if c^.y < y then + cmd := cmd or cCommandDown + else if c^.y > y then + cmd := cmd or cCommandUp; + end; + + Hunt := cmd; +end; + + +function DirectionOK( c : PCharacter; dir : Byte ) : Boolean; +begin + case dir of + cDirectionUp: + DirectionOK := PositionOK( c^.x, c^.y - 8); + cDirectionUpRight: + DirectionOK := PositionOK( c^.x + 8, c^.y - 8); + cDirectionRight: + DirectionOK := PositionOK( c^.x + 8, c^.y); + cDirectionDownRight: + DirectionOK := PositionOK( c^.x + 8, c^.y + 8); + cDirectionDown: + DirectionOK := PositionOK( c^.x, c^.y + 8); + cDirectionDownLeft: + DirectionOK := PositionOK( c^.x - 8, c^.y + 8); + cDirectionLeft: + DirectionOK := PositionOK( c^.x - 8, c^.y); + cDirectionUpLeft: + DirectionOK := PositionOK( c^.x - 8, c^.y - 8); + end; +end; + + +function BrightWalk( c : PCharacter ) : Byte; +begin + if c^.tryRight then + begin + if DirectionOK( c, (c^.currentDir + 7) mod 8) then + begin + c^.currentDir := (c^.currentDir + 7) mod 8; + Dec( c^.turns); + if c^.turns = 0 then + c^.detouring := False; + end + else if not DirectionOK( c, c^.currentDir) then + begin + c^.currentDir := (c^.currentDir + 1) mod 8; + Inc( c^.turns); + if c^.turns = 4 then + begin + c^.tryRight := False; + c^.detouring := False; + c^.turns := 0; + end; + end; + end + else begin + if DirectionOK( c, (c^.currentDir + 1) mod 8) then + begin + c^.currentDir := (c^.currentDir + 1) mod 8; + Dec( c^.turns); + if c^.turns = 0 then + c^.detouring := False; + end + else if not DirectionOK( c, c^.currentDir) then + begin + c^.currentDir := (c^.currentDir + 7) mod 8; + Inc( c^.turns); + if c^.turns = 4 then + begin + c^.tryRight := True; + c^.detouring := False; + c^.turns := 0; + end; + end; + end; + BrightWalk := cCmdFromDirection[ c^.currentDir]; +end; + + +procedure Detour( c : PCharacter ); +begin + c^.detouring := True; + c^.turns := 1; + if c^.tryRight then + c^.currentDir := (cDirectionFromCmd[ c^.command] + 1) mod 8 + else + c^.currentDir := (cDirectionFromCmd[ c^.command] + 7) mod 8; +end; + + +procedure MoveBadGuys( fastPhase : Boolean ); +var c : PCharacter; + roll : Integer; +begin + c := gCharacters; + while c <> nil do + begin + with c^ do + begin + if (id = cIdEvil) and + not dead and + (invincibility <= 0) and + not sleeping then + begin + roll := Random( 16); + if detouring then + cmd := BrightWalk( c) + else if delay > 0 then + Dec( delay) + else begin + if roll < tracking then + cmd := Hunt( c) + else if roll < moving then + cmd := cCmdFromDirection[ Random( 8)] + else + cmd := 0; + delay := actionDelay; + end; + command := cmd; + + if visible and + (gun.lock = 0) and + (roll < shooting) and + FacingPlayer( c) then + Shoot( c) + else begin + if not visible then + gun.lock := 40; + if cmd <> 0 then + begin + if not MoveCharacter( c, cmd, fastPhase) and + not detouring then + Detour( c); + end; + end; + end + else if sleeping then + gun.lock := 40; + end; + c := c^.next; + end; +end; + + +procedure MoveAllCharacters; +var c : PCharacter; +begin + c := gCharacters; + while c <> nil do + begin + with c^ do + begin + if not dead and visible then + MoveCharacter( c, command, False); + end; + c := c^.next; + end; +end; + + +procedure RelocateBadGuys; +var c : PCharacter; + count : Integer; +begin + c := gCharacters; + count := 0; + while c <> nil do + begin + with c^ do + begin + if id = cIdEvil then + begin + if not dead and + sleeping and + not isTarget and + (distanceToPlayer > cRelocationDistance) then + c := RemoveCharacter( c) + else begin + Inc( count); + c := next; + end; + end + else + c := next; + end; + end; + while count < gBadGuyCount do + begin + MakeAnyBadGuy; + Inc( count); + end; +end; + + +end. diff --git a/gamearea.pas b/gamearea.pas new file mode 100644 index 0000000..cc528b4 --- /dev/null +++ b/gamearea.pas @@ -0,0 +1,565 @@ +unit GameArea; + + +interface + + uses Globals; + + +const + + cNoWalk = 128; + cNoWalkLeft = 64; + cBkgFlagMask = 63; + cTrigger = 256; + + cBkgFloor = 0; + cBkgCross = 1 or cNoWalk; + cBkgRightT = 2 or cNoWalkLeft; + cBkgLeftT = 3 or cNoWalk; + cBkgUpT = 4 or cNoWalk; + cBkgDownT = 5 or cNoWalk; + cBkgVert = 6 or cNoWalkLeft; + cBkgDownRight = 7 or cNoWalkLeft; + cBkgDownLeft = 8 or cNoWalk; + cBkgDownEnd = 9 or cNoWalkLeft; + cBkgUpRight = 10 or cNoWalkLeft; + cBkgUpLeft = 11 or cNoWalk; + cBkgUpEnd = 12 or cNoWalkLeft; + cBkgHorz = 13 or cNoWalk; + cBkgRightEnd = 14 or cNoWalk; + cBkgLeftEnd = 15 or cNoWalk; + cBkgBelowWall = 16; + cBkgRightOfWall = 17; + cBkgBelowAndRightOfWall = 18; + cBkgHorzDoor = 19 or cNoWalk; + cBkgShadow = 20; + cBkgShadowRightT = 21; + cBkgShadowVert = 22; + cBkgShadowDownRight = 23; + cBkgShadowDownEnd = 24; + cBkgShadowUpRight = 25; + cBkgShadowUpEnd = 26; + cBkgFloorSkull = 27; + cBkgFloorBlood = 28; + cBkgFloorCrater = 29; + cBkgHorz1 = 31 or cNoWalk; + cBkgHorz2 = 32 or cNoWalk; + cBkgHorz3 = 33 or cNoWalk; + cBkgHorz4 = 34 or cNoWalk; + cBkgHorz5 = 35 or cNoWalk; + cBkgFloorItem1 = 36; + cBkgFloorItem2 = 37; + cBkgFloorItem3 = 38; + cBkgFloorItem4 = 39; + cBkgFloorItem5 = 40; + cBkgFloorItem6 = 41; + cBkgFloorItem7 = 42; + cBkgBlueBoxRemains = 43; + cBkgGreyBoxRemains = 44; + cBkgChaosBoxRemains = 45; + cBkgFloorDocs = 46; + cBkgBoxRemains = 47; + cBkgCrate = 48 or cNoWalk; + cBkgCrateRemains = 49; + cBkgFloorArmorAddOn = 50; + cBkgFloorAxe = 51; + cBkgFloorFolder = 52; + cBkgFloorDisk = 53; + cBkgFloorCircuit = 54; + cBkgFloorTeddy = 55; + + cBkgPicMax = 55; + + cBkgChaosBox = (cBkgPicMax + 1) or cNoWalk; + cBkgExit = (cBkgPicMax + 2); + cBkgBlueBox = (cBkgPicMax + 3) or cNoWalk; + cBkgGreyBox = (cBkgPicMax + 4) or cNoWalk; + cBkgFloorFan = (cBkgPicMax + 5); + cBkgBox = (cBkgPicMax + 6) or cNoWalk; + + + cBkgHidden = [cBkgShadow..cBkgShadowUpEnd]; + cBkgSpecial = [cBkgChaosBox and cBkgFlagMask, cBkgExit, cBkgFloorFan, + cBkgBlueBox and cBkgFlagMask, cBkgGreyBox and cBkgFlagMask, + cBkgBox and cBkgFlagMask]; + + cwCharMap = 16; + chCharMap = 24; + cwWalkMap = 16; + chWalkMap = 24; + cwWorldMap = 32; + chWorldMap = 24; + + +type + + WorldParams = + record + architectureStyle, + wallCount, + wallLength, + roomCount, + detailDensity : Integer; + end; + + +procedure BuildWorld( params : WorldParams; mission, targets, objects : Integer ); + + +const + + cxFlagMin = cxMin*2; + cxFlagMax = cxMax*2 + 1; + +type + + FlagMap = array [cxFlagMin..cxFlagMax, cyMin..cyMax] of Boolean; + CounterMap = array [cxFlagMin..cxFlagMax, cyMin..cyMax] of Byte; + StructureMap = array [cxMin..cxMax, cyMin..cyMax] of + record structure : Byte; wreckagePic : Integer end; + + +var + + gWorld : array [cxMin..cxMax, cyMin..cyMax] of Word; + gCharacterMap : ^CounterMap; + gStructureMap : ^StructureMap; + gNoWalk : ^FlagMap; + + +implementation + + uses SPX_Fnc; + + +function FindPicIndex( x, y : Integer ) : Byte; + + function Anything( x, y : Integer ) : Boolean; + begin + if Range( x, y, cxMin, cyMin, cxMax, cyMax) then + Anything := gWorld[ x, y] and (cNoWalk or cNoWalkLeft) <> 0 + else + Anything := False; + end; + +var this : Byte; + up, down, left, right : Boolean; +begin + this := gWorld[ x, y]; + if this = 0 then + begin + if Anything( x, y-1) then + FindPicIndex := cBkgBelowWall + else + FindPicIndex := cBkgFloor; + Exit; + end; + + up := Anything( x, y-1); + down := Anything( x, y+1); + left := Anything( x-1, y); + right := Anything( x+1, y); + + if up and down and left and right then + FindPicIndex := cBkgCross + else if up and down and left then + FindPicIndex := cBkgRightT + else if up and down and right then + FindPicIndex := cBkgLeftT + else if down and left and right then + FindPicIndex := cBkgUpT + else if up and left and right then + FindPicIndex := cBkgDownT + else if up and down then + FindPicIndex := cBkgVert + else if up and left then + FindPicIndex := cBkgDownRight + else if up and right then + FindPicIndex := cBkgDownLeft + else if up then + FindPicIndex := cBkgDownEnd + else if down and left then + FindPicIndex := cBkgUpRight + else if down and right then + FindPicIndex := cBkgUpLeft + else if down then + FindPicIndex := cBkgUpEnd + else if left and right then + FindPicIndex := cBkgHorz + else if left then + FindPicIndex := cBkgRightEnd + else + FindPicIndex := cBkgLeftEnd; +end; + + +function AreaClear( xOrigin, yOrigin, width, height : Integer ) : Boolean; +var x, y : Integer; +begin + for x := xOrigin to xOrigin + width do + for y := yOrigin to yOrigin + height do + if gWorld[ x, y] <> 0 then + begin + AreaClear := False; + Exit; + end; + AreaClear := True; +end; + + +procedure Build( x, y, d, length : Integer ); +var l, + nx, ny, + dx, dy : Integer; +begin + if length <= 0 then + Exit; + + dx := 0; + dy := 0; + case d of + 0: dx := -1; + 1: dx := 1; + 2: dy := -1; + 3: dy := 1; + end; + nx := 2*(x+dx); + ny := 2*(y+dy); + if Range( 2*x, 2*y, cxMin, cyMin, cxMax, cyMax) and + Range( nx, ny, cxMin, cyMin, cxMax, cyMax) and + Range( nx-dx, ny-dy, cxMin, cyMin, cxMax, cyMax) and + (gWorld[ nx, ny] = 0) then + begin + gWorld[ 2*x, 2*y] := cNoWalk; + gWorld[ nx-dx, ny-dy] := cNoWalk; + gWorld[ nx, ny] := cNoWalk; + Inc( x, dx); + Inc( y, dy); + Dec( length); + if Random(4) = 0 then + begin + l := Random( length); + Build( x, y, Random(4), l); + Dec( length, l); + end; + Build( x, y, d, length); + end; +end; + + +procedure BuildWall( wallLength : Integer ); +var x, y, d : Integer; +begin + x := (Random( cxMax - cxMin) + cxMin) div 2; + y := (Random( cyMax - cyMin) + cyMin) div 2; + if (gWorld[ 2*x, 2*y] = 0) or + (x = cxMin) or (x = cxMax) or (y = cyMin) or (y = cyMax) then + Build( x, y, Random( 4), wallLength); +end; + + +procedure MakeDoor( x, y, xOffs, yOffs : Integer ); +begin + {if (xOffs > 0) and not Odd( xOffs) then + Dec( xOffs); + if (yOffs > 0) and not Odd( yOffs) then + Dec( yOffs);} + gWorld[ x + xOffs, y + yOffs] := 1; +end; + + +procedure MakeRoom( xOrigin, yOrigin, width, height, doors : Integer ); +var x, y : Integer; +begin + for y := yOrigin to yOrigin + height do + begin + gWorld[ xOrigin, y] := cNoWalk; + gWorld[ xOrigin + width, y] := cNoWalk; + end; + for x := xOrigin+1 to xOrigin + width-1 do + begin + gWorld[ x, yOrigin] := cNoWalk; + gWorld[ x, yOrigin + height] := cNoWalk; + for y := yOrigin+1 to yOrigin + height-1 do + gWorld[ x, y] := 1; + end; + if doors and 1 <> 0 then + MakeDoor( xOrigin, yOrigin, 0, height div 2); + if doors and 2 <> 0 then + MakeDoor( xOrigin + width, yOrigin, 0, height div 2); + if doors and 4 <> 0 then + MakeDoor( xOrigin, yOrigin, width div 2, 0); + if doors and 8 <> 0 then + MakeDoor( xOrigin, yOrigin + height, width div 2, 0); +end; + + +procedure BuildRoom; +var x, y, w, h : Integer; +begin + x := Random( cxMax - cxMin - 10) + cxMin; if Odd( x) then Inc( x); + y := Random( cyMax - cyMin - 10) + cyMin; if Odd( y) then Inc( y); + w := 2*Random( 2) + 4; + h := 2*Random( 2) + 4; + if Range( x, y, cxMin + 2, cyMin + 2, cxMax - 2, cyMax - 2) and + Range( x + w, y + h, cxMin + 2, cyMin + 2, cxMax - 2, cyMax - 2) and + AreaClear( x, y, w, h) then + MakeRoom( x, y, w, h, Random( 15) + 1); +end; + + + +procedure RemoveFilling; +var x, y : Integer; +begin + for x := cxMin to cxMax do + for y := cyMin to cyMax do + if gWorld[ x, y] = 1 then + gWorld[ x, y] := 0; +end; + + +procedure FixWorld; +var x, y, pic : Integer; +begin + RemoveFilling; + for x := cxMin to cxMax do + for y := cyMin to cyMax do + begin + pic := FindPicIndex( x, y); + gWorld[ x, y] := pic; + end; + for x := cxMin to cxMax-1 do + for y := cxMin to cyMax-1 do + begin + if gWorld[ x, y] = cBkgRightEnd then + begin + gWorld[ x+1, y] := cBkgRightOfWall; + gWorld[ x+1, y+1] := cBkgBelowAndRightOfWall; + end; + end; +end; + + +procedure AddDetails( density : Integer ); +var x, y, pic : Integer; +begin + for x := cxMin+1 to cxMax-1 do + for y := cxMin+1 to cyMax-1 do + begin + if gWorld[ x, y] = cBkgFloor then + case Random( density) of + 1: gWorld[ x, y] := cBkgFloorSkull; + 2: gWorld[ x, y] := cBkgFloorBlood; + 3: gWorld[ x, y] := cBkgFloorCrater; + 4: if Odd( x) = Odd( y) then + gWorld[ x, y] := cBkgFloorFan; + 5: if (gWorld[ x, y+1] and (cNoWalk or cNoWalkLeft) = 0) and + (gWorld[ x, y-1] and (cNoWalk or cNoWalkLeft) = 0) then + begin + gWorld[ x, y] := cBkgBlueBox; + gStructureMap^[ x, y].structure := 100; + gStructureMap^[ x, y].wreckagePic := cBkgBlueBoxRemains; + end; + 6: if (gWorld[ x, y+1] and (cNoWalk or cNoWalkLeft) = 0) and + (gWorld[ x, y-1] and (cNoWalk or cNoWalkLeft) = 0) then + begin + gWorld[ x, y] := cBkgGreyBox; + gStructureMap^[ x, y].structure := 25; + gStructureMap^[ x, y].wreckagePic := cBkgGreyBoxRemains; + end; + 7: if (gWorld[ x, y+1] and (cNoWalk or cNoWalkLeft) = 0) and + (gWorld[ x, y-1] and (cNoWalk or cNoWalkLeft) = 0) then + begin + gWorld[ x, y] := cBkgCrate; + gStructureMap^[ x, y].structure := 25; + gStructureMap^[ x, y].wreckagePic := cBkgCrateRemains; + end; + 8: if (gWorld[ x, y+1] and (cNoWalk or cNoWalkLeft) = 0) and + (gWorld[ x, y-1] and (cNoWalk or cNoWalkLeft) = 0) then + begin + gWorld[ x, y] := cBkgBox; + gStructureMap^[ x, y].structure := 10; + gStructureMap^[ x, y].wreckagePic := cBkgBoxRemains; + end; + end + else if gWorld[ x, y] = cBkgHorz then + case Random( 15) of + 1: gWorld[ x, y] := cBkgHorz1; + 2: gWorld[ x, y] := cBkgHorz2; + 3: gWorld[ x, y] := cBkgHorz3; + 4: gWorld[ x, y] := cBkgHorz4; + 5: gWorld[ x, y] := cBkgHorz5; + end; + end; +end; + + +function AddOneObject( what, structure : Byte; wreckPic : Integer ) : Boolean; +var attempts, + x, y : Integer; +begin + AddOneObject := True; + attempts := 0; + repeat + x := Random( cxMax - cxMin) + cxMin; + y := Random( cyMax - cyMin) + cyMin; + if gWorld[ x, y] = cBkgFloor then + begin + gWorld[ x, y] := what; + gStructureMap^[ x, y].structure := structure; + gStructureMap^[ x, y].wreckagePic := wreckPic; + Exit; + end; + Inc( attempts); + until attempts > 100; + AddOneObject := False; +end; + + +procedure AddExit; +var i : Integer; +begin + for i := 1 to 100 do + if AddOneObject( cBkgExit, 0, 0) then + Exit; + gWorld[ cxMin + 1, cyMin + 1] := cBkgFloor; + gWorld[ cxMin + 2, cyMin + 1] := cBkgFloor; + gWorld[ cxMin + 3, cyMin + 1] := cBkgFloor; + gWorld[ cxMin + 1, cyMin + 2] := cBkgFloor; + gWorld[ cxMin + 2, cyMin + 2] := cBkgExit; + gWorld[ cxMin + 3, cyMin + 2] := cBkgFloor; + gWorld[ cxMin + 1, cyMin + 3] := cBkgFloor; + gWorld[ cxMin + 2, cyMin + 3] := cBkgFloor; + gWorld[ cxMin + 3, cyMin + 3] := cBkgFloor; + FixWorld; +end; + + +procedure AddTargets( mission, targets : Integer ); +var i : Integer; +begin + for i := 1 to targets do + if AddOneObject( cBkgChaosBox, 250, cBkgChaosBoxRemains) then + Inc( gTargetsLeft); +end; + + +procedure AddObjects( mission, objects : Integer ); + + function Max( a, b : Integer ) : Integer; + begin + if a > b then + Max := a + else + Max := b; + end; + +var i, count : Integer; +begin + count := 0; + for i := 1 to Max( 3*objects, 10) do + case Random( 5) of + 0: if AddOneObject( cBkgFloorDocs, 0, 0) then Inc( count); + 1: if AddOneObject( cBkgFloorFolder, 0, 0) then Inc( count); + 2: if AddOneObject( cBkgFloorDisk, 0, 0) then Inc( count); + 3: if AddOneObject( cBkgFloorCircuit, 0, 0) then Inc( count); + 4: if AddOneObject( cBkgFloorTeddy, 0, 0) then Inc( count); + end; + if count > objects*2 then + gObjectsToCollect := objects + else + gObjectsToCollect := count div 2; + + for i := 1 to 4 do + case Random( 5) of + 0: AddOneObject( cBkgFloorItem1, 0, 0); + 1: AddOneObject( cBkgFloorItem2, 0, 0); + 2: AddOneObject( cBkgFloorItem3, 0, 0); + 3: AddOneObject( cBkgFloorItem4, 0, 0); + 4: AddOneObject( cBkgFloorItem5, 0, 0); + end; + + for i := 0 to mission do + AddOneObject( cBkgFloorItem6, 0, 0); + + AddOneObject( cBkgFloorArmorAddOn, 0, 0); + AddOneObject( cBkgFloorArmorAddOn, 0, 0); + AddOneObject( cBkgFloorItem7, 0, 0); + AddOneObject( cBkgFloorItem7, 0, 0); + + if gPlayerData[0].playing and gPlayerData[1].playing then + begin + for i := 1 to 4 do + case Random( 5) of + 0: AddOneObject( cBkgFloorItem1, 0, 0); + 1: AddOneObject( cBkgFloorItem2, 0, 0); + 2: AddOneObject( cBkgFloorItem3, 0, 0); + 3: AddOneObject( cBkgFloorItem4, 0, 0); + 4: AddOneObject( cBkgFloorItem5, 0, 0); + end; + + for i := 0 to mission do + AddOneObject( cBkgFloorItem6, 0, 0); + + AddOneObject( cBkgFloorArmorAddOn, 0, 0); + AddOneObject( cBkgFloorArmorAddOn, 0, 0); + AddOneObject( cBkgFloorItem7, 0, 0); + AddOneObject( cBkgFloorItem7, 0, 0); + end; +end; + + +procedure SetupNoWalkMap; +var x, y : Integer; +begin + FillChar( gNoWalk^, SizeOf( gNoWalk^), 0); + for x := cxMin to cxMax do + for y := cyMin to cyMax do + begin + if gWorld[ x, y] and (cNoWalkLeft or cNoWalk) <> 0 then + gNoWalk^[ 2*x, y] := True; + if gWorld[ x, y] and cNoWalk <> 0 then + gNoWalk^[ 2*x + 1, y] := True; + end; + { Far right is a vertical wall, they do not have the cNoWalk flag set - + technically there's room enough to walk immediately to the right of + that "edge"-wall. However, I don't want any baddies to be placed there + so mark it as occupied... } + for y := cyMin to cyMax do + gNoWalk^[ cxFlagMax, y] := True; +end; + + +procedure BuildWorld( params : WorldParams; mission, targets, objects : Integer ); +var i : Integer; +begin + FillChar( gWorld, SizeOf( gWorld), 0); + FillChar( gStructureMap^, SizeOf( gStructureMap^), 0); + for i := cyMin to cyMax do + begin + gWorld[ cxMin, i] := cNoWalk; + gWorld[ cxMax, i] := cNoWalk; + end; + for i := cxMin to cxMax do + begin + gWorld[ i, cyMin] := cNoWalk; + gWorld[ i, cyMax] := cNoWalk; + end; + + for i := 1 to params.roomCount do + BuildRoom; + for i := 0 to params.wallCount do + BuildWall( params.wallLength); + + FixWorld; + AddExit; + AddTargets( mission, targets); + AddDetails( params.detailDensity); + AddObjects( mission, objects); + SetupNoWalkMap; +end; + + +end. diff --git a/globals.pas b/globals.pas new file mode 100644 index 0000000..7bbc4b1 --- /dev/null +++ b/globals.pas @@ -0,0 +1,378 @@ +unit Globals; + + +interface + + uses SPX_VGA, + Keyboard, Sounds, Ini; + + +const + + cxMin = 10; + cxMax = 70; + cyMin = 10; + cyMax = 70; + + cBlack = 1; + cYellow = 17; + cOrange = 19; + cBrightRed = 21; + cRed = 23; + cDarkRed = 25; + cWhite = 255; + cGray = 37; + + cCommandUp = 2; + cCommandDown = 1; + cCommandLeft = 4; + cCommandRight = 8; + cCommandShoot = 16; + cCommandSwitch = 32; + cCommandFreeze = 64; + cCommandNoTurn = 128; + cCommandMoving = 15; + cCommandFiring = 48; + cCommandNoMove = 96; { 112 to disallow movement whilst firing } + + cDirectionUp = 0; + cDirectionUpRight = 1; + cDirectionRight = 2; + cDirectionDownRight = 3; + cDirectionDown = 4; + cDirectionDownLeft = 5; + cDirectionLeft = 6; + cDirectionUpLeft = 7; + + cDirectionFromCmd : array [0..15] of Byte = + ( 0, cDirectionDown, cDirectionUp, 0, + cDirectionLeft, cDirectionDownLeft, cDirectionUpLeft, 0, + cDirectionRight, cDirectionDownRight, cDirectionUpRight, 0, + 0, 0, 0, 0 ); + + cCmdFromDirection : array [0..7] of Byte = + ( cCommandUp, + cCommandUp + cCommandRight, + cCommandRight, + cCommandDown + cCommandRight, + cCommandDown, + cCommandDown + cCommandLeft, + cCommandLeft, + cCommandUp + cCommandLeft ); + + cMaxHorizontalDistance = 250; + cMaxVerticalDistance = 150; + + cProximityDistance = 100; + cRelocationDistance = 400; + cLargeDistance = 2500; + + cCharacterHeight = 20; + cCharacterWidth = 15; + + cMaxWeaponry = 5; + cFireBallsPerExplosion = 5; + + +type + + WeaponryData = + array [0..cMaxWeaponry] of + record + kind : Byte; + ammo : Integer; + end; + + ScoreData = + record + score, + kills, + targets, + objects, + closeCombat, + demolition, + shotsFired, + hits, + hitsTaken, + time : LongInt; + end; + + PlayerData = + record + mission, + total : ScoreData; + cash : LongInt; + missions, + armor : Integer; + lives : Integer; + closeCombat : Integer; + weapons : WeaponryData; + playing : Boolean; + hero : Byte; + button2Down, + button2Movement : Boolean; + completed : Boolean; + end; + + HeroInfo = + record + name : String[15]; + body : Byte; + face : Byte; + end; + + KeyControls = + record + up, + down, + left, + right, + shoot, + switch : Byte; + {freeze, + noTurn : Byte;} + rotate : Boolean; + stick : Byte; + end; + + GunRec = + record + name : String[20]; + pic : Word; + range : Integer; + rate : Byte; + power : Byte; + speed : Integer; + radius : Integer; + bulletStyle : Byte; + animated : Byte; + sound : Byte; + areaEffect : Integer; + end; + + +const + + hero1Keys : KeyControls = + ( up: keyArrowUp; + down: keyArrowDown; + left: keyArrowLeft; + right: keyArrowRight; + shoot: keyLeftCtrl; + switch: keyEnter; + rotate: False; + stick: 0 ); + + hero2Keys : KeyControls = + ( up: keyKeypad8; + down: keyKeypad2; + left: keyKeypad4; + right: keyKeypad6; + shoot: keyRightCtrl; + switch: keySpace; + rotate: False; + stick: 0 ); + + cAnimationNone = 0; + cAnimationRandom = 1; + cAnimationSequence = 2; + + cGunStyles : array [1..10] of GunRec = + ( ( name: 'PowerGun'; + pic: 134; + range: 70; + rate: 40; + power: 60; + speed: 10; + radius: 5; + bulletStyle: 7; + animated: cAnimationNone; + sound: cBigGunSound ), + ( name: 'Blaster'; + pic: 135; + range: 50; + rate: 12; + power: 10; + speed: 8; + radius: 4; + bulletStyle: 0; + animated: cAnimationNone; + sound: cSmallGunSound ), + ( name: 'MiniGun'; + pic: 136; + range: 30; + rate: 7; + power: 6; + speed: 12; + radius: 3; + bulletStyle: 2; + animated: cAnimationNone; + sound: cMinigunSound ), + ( name: 'Flamer'; + pic: 137; + range: 25; + rate: 7; + power: 10; + speed: 3; + radius: 10; + bulletStyle: 3; + animated: cAnimationRandom; + sound: cFlamerSound ), + ( name: 'Launcher'; + pic: 138; + range: 50; + rate: 30; + power: 40; + speed: 4; + radius: 1; + bulletStyle: 4; + animated: cAnimationSequence; + sound: cLaunchSound ), + ( name: 'DumbGun'; + pic: 134; + range: 110; + rate: 40; + power: 5; + speed: 4; + radius: 0; + bulletStyle: 1; + animated: cAnimationNone; + sound: cSmallGunSound ), + ( name: 'Gun'; + pic: 134; + range: 110; + rate: 30; + power: 3; + speed: 8; + radius: 0; + bulletStyle: 6; + animated: cAnimationNone; + sound: cSmallGunSound ), + ( name: 'Lazer'; + pic: 134; + range: 100; + rate: 65; + power: 10; + speed: 10; + radius: 0; + bulletStyle: 5; + animated: cAnimationNone; + sound: cLaserSound ), + ( name: 'TurboLazer'; + pic: 134; + range: 100; + rate: 35; + power: 8; + speed: 10; + radius: 0; + bulletStyle: 5; + animated: cAnimationNone; + sound: cLaserSound ), + ( name: 'Zapper'; + pic: 351; + range: 30; + rate: 7; + power: 10; + speed: 12; + radius: 6; + bulletStyle: 8; + animated: cAnimationSequence; + sound: cLaserSound ) + ); + + cPowerGun = 1; + cBlaster = 2; + cSprayer = 3; + cFlamer = 4; + cGrenade = 5; + cGoonGun = 6; + cGoonGun2 = 7; + cGoonGun3 = 8; + cGoonGun4 = 9; + cMegaGun = 10; + + cBlueBody = 0; + cBrownBody = 1; + cGreyBody = 2; + cRedBody = 3; + cBlackBody = 4; + cMaxBody = 4; + + cFaceJones = 0; + cFaceIce = 1; + cFaceWarBaby = 2; + cFaceGrunt = 3; + cFaceMechGrunt = 4; + cFaceGrunt2 = 5; + cFaceBlondGrunt = 6; + cFaceBigBadGuy = 7; + cMaxFace = 7; + + cGunDogs = 0; + cGunMax = 0; + + cHeroJones = 0; + cHeroIce = 1; + cHeroWarBaby = 2; + cMaxHeros = 2; + + cHeroInfo : array [0..cMaxHeros] of HeroInfo = + ( ( name: 'Jones'; + body: cBlueBody; + face: cFaceJones + ), + ( name: 'Ice'; + body: cBlueBody; + face: cFaceIce + ), + ( name: 'WarBaby'; + body: cBlueBody; + face: cFaceWarBaby + ) + ); + + gCampaign : Word = 0; + + cBaddieGoon = 0; + cBaddieMeanGoon = 1; + cBaddieKiller = 2; + cBaddieRobot = 3; + cBaddieLittleBad = 4; + cBaddieBigBad = 5; + + cPoolLives = 1; + cPoolCash = 2; + + gFreeMovement : Boolean = False; + g2PlayerPool : Integer = cPoolLives or cPoolCash; + gMapKey : Byte = keyF1; + + +var + + gPalette : RGBList; + + gPlayerData : array [0..1] of PlayerData; + + gGameOver : Boolean; + gTargetsLeft, gObjectsToCollect, gMinimumKills : Integer; + gMissionTime : LongInt; + gAlert1, + gAlert2 : Boolean; + gFrameCounter : LongInt; + gMissionTargets : Set of Byte; + gVBlankCounter : LongInt; + gBadGuyCount : Integer; + gBaddieProbs : array [cBaddieGoon..cBaddieBigBad] of Byte; + + gAutoMap : array [ cxMin..cxMax, cyMin..cyMax] of Byte; + + gIniSettings : PSection; + gCyclesPerFrame : Integer; + + + +implementation + + +end. diff --git a/hallfame.pas b/hallfame.pas new file mode 100644 index 0000000..03bcec8 --- /dev/null +++ b/hallfame.pas @@ -0,0 +1,356 @@ +unit HallFame; + + +interface + + +procedure DisplayHallOfFame; +procedure ApplyForHallOfFame( index : Integer ); + + +implementation + + uses SPX_VGA, SPX_Txt, SPX_Fnc, Crt, + Keyboard, Screen, Globals, Stick, BigFont, Pics, Sounds; + + +const + + cHallMax = 20; + + cHallOfFameScore = 0; + cHallOfFameMissions = 0; + cHallOfFameKills = 0; + cHallOfFameCloseCombat = 0; + + +type + + HallEntry = + record + name : String[20]; + score, + missions, + kills, + combat, + shotsFired, + hits, + hitsTaken, + campaign, + demolition, + cash : LongInt; + end; + + HallList = array [1..cHallMax] of HallEntry; + HallPtr = ^HallList; + + +function LoadHallOfFame : HallPtr; +var h : HallPtr; + f : File of HallEntry; + i : Integer; +begin + New( h); + FillChar( h^, SizeOf( h^), 0); + for i := 1 to cHallMax do + h^[i].name := '---'; + LoadHallOfFame := h; + + Assign( f, 'DOGS.HI'); + Reset( f); + if IOResult <> 0 then + Exit; + + i := 1; + while not Eof( f) do + begin + Read( f, h^[i]); + Inc( i); + end; + Close( f); +end; + + +procedure WriteHallOfFame( h : HallPtr ); +var f : File of HallEntry; + i : Integer; +begin + Assign( f, 'DOGS.HI'); + Rewrite( f); + if IOResult <> 0 then + Exit; + + for i := 1 to cHallMax do + Write( f, h^[i]); + Close( f); +end; + + +procedure SortHallOfFame( h : HallPtr; by : Byte ); + + function LessEntry( i, j : Integer ) : Boolean; + begin + case by of + cHallOfFameScore: + LessEntry := h^[i].score < h^[j].score; + { + cHallOfFameMissions: + Less := h^[i].score < h^[j].score; + cHallOfFameScore: + Less := h^[i].score < h^[j].score; + cHallOfFameScore: + Less := h^[i].score < h^[j].score; + } + end; + end; + + procedure SwapEntry( i, j : Integer ); + var e : HallEntry; + begin + e := h^[i]; + h^[i] := h^[j]; + h^[j] := e; + end; + +var i, j : Integer; +begin + for i := 1 to cHallMax - 1 do + for j := i + 1 to cHallMax do + if LessEntry( i, j) then + SwapEntry( i, j); +end; + + +procedure DisplayHallOfFame; + + procedure ClearDisplay; + begin + Bar( 32, 60, 287, 179, cBlack); + end; + + procedure WriteLine( var x, y : Integer; s : String; c : Byte ); + var i : Integer; + begin + i := 1; + while (i <= Length( s)) and not AnyKeyDown and not StickButtonPressed do + begin + if s[i] < #127 then + begin + PutLetter( 40 + x, y, cWhite, s[i]); + VSinc; + VSinc; + PutLetter( 40 + x, y, cYellow, s[i]); + VSinc; + VSinc; + PutLetter( 40 + x, y, c, s[i]); + Inc( x, StLen( s[i])); + end + else + x := 5*(Ord( s[i]) - 127); {40*((x + 40) div 40);} + Inc( i); + end; + end; + + procedure WriteOneEntry( var y : Integer; index : Integer; var h: HallEntry ); + var x : Integer; + begin + if h.score <= 0 then + Exit; + x := 0; + WriteLine( x, y, St( index) + '. ', cBrightRed); + WriteLine( x, y, h.name, cYellow); + if AnyKeyDown or StickButtonPressed then + Exit; + x := 0; + Inc( y, 8); + WriteLine( x, y, 'Score: ', cOrange); + WriteLine( x, y, St( h.score), cBrightRed); + if AnyKeyDown or StickButtonPressed then + Exit; + x := 100; + WriteLine( x, y, 'Missions: ', cOrange); + WriteLine( x, y, St( h.missions), cBrightRed); + if AnyKeyDown or StickButtonPressed then + Exit; + x := 0; + Inc( y, 8); + WriteLine( x, y, 'Kills: ', cOrange); + WriteLine( x, y, St( h.kills), cBrightRed); + if AnyKeyDown or StickButtonPressed then + Exit; + x := 50; + WriteLine( x, y, 'Ninja: ', cOrange); + WriteLine( x, y, St( h.combat), cBrightRed); + if AnyKeyDown or StickButtonPressed then + Exit; + if h.shotsFired > 0 then + begin + x := 100; + WriteLine( x, y, 'Accuracy: ', cOrange); + WriteLine( x, y, St( h.hits), cBrightRed); + WriteLine( x, y, '/', cOrange); + WriteLine( x, y, St( h.shotsFired), cBrightRed); + WriteLine( x, y, ', ', cOrange); + WriteLine( x, y, St( (100*h.hits) div h.shotsFired), cBrightRed); + WriteLine( x, y, '%', cOrange); + if AnyKeyDown or StickButtonPressed then + Exit; + end; + Inc( y, 10); + end; + + procedure Display( h : HallPtr ); + var y : Integer; + i : Integer; + begin + i := 1; + while not AnyKeyDown and not StickButtonPressed do + begin + y := 65; + while (i <= cHallMax) and (y < 160) do + begin + WriteOneEntry( y, i, h^[i]); + Inc( i); + end; + if i > cHallMax then + i := 1; + if not AnyKeyDown and not StickButtonPressed then + for y := 1 to 25 do + VSinc; + ClearDisplay; + end; + end; + +var h : HallPtr; +begin + SetPageActive( 1); + FillScreen( cSteelPlate); + ClearDisplay; + + BigText( 20, 25, 'MEAN DOGS'); + + PlayMusic( gHallOfFameSong); + FadeIn( 5, gPalette); + + h := LoadHallOfFame; + SortHallOfFame( h, cHallOfFameScore); + Display( h); + Dispose( h); + FadeOut( 5, gPalette); +end; + + +function EnterHallOfFame( h : HallPtr; + by : Byte; + index : Integer; + name : String ) : Boolean; + + + function Less( i, index : Integer ) : Boolean; + begin + case by of + cHallOfFameScore: + Less := h^[i].score < gPlayerData[ index].total.score; + end; + end; + + procedure Insert( index, i : Integer ); + var j : Integer; + begin + for j := cHallMax downto i + 1 do + h^[j] := h^[j-1]; + h^[i].name := name; + h^[i].score := gPlayerData[ index].total.score; + h^[i].missions := gPlayerData[ index].missions; + h^[i].kills := gPlayerData[ index].total.kills; + h^[i].combat := gPlayerData[ index].total.closeCombat; + h^[i].campaign := gCampaign; + h^[i].shotsFired := gPlayerData[ index].total.shotsFired; + h^[i].hits := gPlayerData[ index].total.hits; + h^[i].hitsTaken := gPlayerData[ index].total.hitsTaken; + h^[i].demolition := gPlayerData[ index].total.demolition; + h^[i].cash := gPlayerData[ index].cash; + end; + +var i : Integer; +begin + i := 1; + while i <= cHallMax do + begin + if Less( i, index) then + begin + if name <> '' then + Insert( index, i); + EnterHallOfFame := True; + Exit; + end; + Inc( i); + end; + EnterHallOfFame := False; +end; + + +procedure ApplyForHallOfFame( index : Integer ); + + function GetString( x, y, max : Integer ) : String; + var key : Char; + const s : String = ''; + begin + PutLetter( x, y, cBrightRed, s + '_'); + repeat + key := ReadKey; + if key = #0 then + ReadKey + else begin + if key = #8 then + begin + if s <> '' then + Delete( s, Length( s), 1); + PutLetter( x, y, cBrightRed, s); + Bar( x + StLen( s), y, 255, y+10, cBlack); + PutLetter( x, y, cBrightRed, s + '_'); + end + else if (key >= ' ') and (key <= 'z') and (Length( s) < max) then + begin + Bar( x + StLen( s), y, 255, y+10, cBlack); + s := s + key; + PutLetter( x, y, cBrightRed, s + '_'); + end; + end; + until key = #13; + if s = '' then + s := 'Anonymous'; + GetString := s; + end; + +var h : HallPtr; + name : String; +begin + h := LoadHallOfFame; + if EnterHallOfFame( h, cHallOfFameScore, index, '') then + begin + RemoveKbdHandler; + FadeOut( 5, gPalette); + SetPageActive( 1); + FillScreen( cSteelPlate); + BigText( 20, 25, 'WOOF WOOF'); + with cHeroInfo[ gPlayerData[ index].hero] do + begin + DrawCharacter( 10, 90, cDirectionDown, cDirectionDownRight, face, body, cGunDogs, cFaceNormal, 0, cGunIdle); + DrawLetter( 12, 115, cWhite, cBlack, name); + end; + Bar( 64, 80, 255, 119, cBlack); + PutLetter( 70, 90, cRed, 'You are one mean Dog! Enter your name'); + FadeIn( 5, gPalette); + PlaySound( cBangSound); + name := GetString( 70, 100, 20); + PlaySound( cBigGunSound); + EnterHallOfFame( h, cHallOfFameScore, index, name); + WriteHallOfFame( h); + InstallKbdHandler; + end; + Dispose( h); +end; + + +end. diff --git a/ini.pas b/ini.pas new file mode 100644 index 0000000..07b4d04 --- /dev/null +++ b/ini.pas @@ -0,0 +1,280 @@ +unit Ini; + + +interface + + +type + + PValue = ^TValue; + TValue = + record + key : String[20]; + value : String[80]; + next : PValue; + end; + + PSection = ^TSection; + TSection = + record + name : String[20]; + settings : PValue; + next : PSection; + end; + + +function Equal( s1, s2 : String ) : Boolean; +function LoadIniFile( name : String; var settings : PSection ) : Boolean; +function FindIniString( settings : PSection; section, key, default : String ) : String; +function FindIniNumber( settings : PSection; section, key : String; default : LongInt ) : LongInt; +function FindIniBoolean( settings : PSection; section, key : String; default : Boolean ) : Boolean; +procedure SetIniString( var settings : PSection; section, key, value : String ); +procedure SetIniNumber( var settings : PSection; section, key : String; value : LongInt ); +procedure SetIniBoolean( var settings : PSection; section, key : String; value : Boolean ); +function GetIniValues( settings : PSection; section : String ) : PValue; +procedure WriteIniFile( name : String; settings : PSection ); +procedure DisposeSettings( var settings : PSection ); + + +implementation + + uses SPX_Fnc; + + +function Equal( s1, s2 : String ) : Boolean; +begin + s1 := Ups( s1); + s2 := Ups( s2); + Equal := s1 = s2; +end; + + +procedure AddSetting( var settings : PSection; section, key, value : String ); +var h : ^PSection; + v : ^PValue; +begin + h := @settings; + while (h^ <> nil) and not Equal( h^^.name, section) do + h := @h^^.next; + if h^ = nil then + begin + New( h^); + h^^.next := nil; + h^^.name := section; + h^^.settings := nil; + end; + v := @h^^.settings; + while v^ <> nil do + v := @v^^.next; + New( v^); + v^^.key := key; + v^^.value := value; + v^^.next := nil; +end; + + +procedure SetSetting( var settings : PSection; section, key, value : String ); +var h : ^PSection; + v : ^PValue; +begin + h := @settings; + while (h^ <> nil) and not Equal( h^^.name, section) do + h := @h^^.next; + if h^ = nil then + begin + New( h^); + h^^.next := nil; + h^^.name := section; + h^^.settings := nil; + end; + v := @h^^.settings; + while (v^ <> nil) and not Equal( v^^.key, key) do + v := @v^^.next; + if v^ = nil then + begin + New( v^); + v^^.next := nil; + end; + v^^.key := key; + v^^.value := value; +end; + + +procedure DisposeSettings( var settings : PSection ); +var s : PSection; + v : PValue; +begin + while settings <> nil do + begin + s := settings; + settings := s^.next; + while s^.settings <> nil do + begin + v := s^.settings; + s^.settings := v^.next; + Dispose( v); + end; + Dispose( s); + end; +end; + + +function GetIniValues( settings : PSection; section : String ) : PValue; +begin + while settings <> nil do + begin + if Equal( settings^.name, section) then + begin + GetIniValues := settings^.settings; + Exit; + end; + settings := settings^.next; + end; + GetIniValues := nil; +end; + + +function FindIniString( settings : PSection; section, key, default : String ) : String; +var v : PValue; +begin + while settings <> nil do + begin + if Equal( settings^.name, section) then + begin + v := settings^.settings; + while v <> nil do + begin + if Equal( v^.key, key) then + begin + FindIniString := v^.value; + Exit; + end; + v := v^.next; + end; + end; + settings := settings^.next; + end; + FindIniString := default; +end; + + +function FindIniNumber( settings : PSection; section, key : String; default : LongInt ) : LongInt; +var s : String; + x : LongInt; + ignore : Integer; +begin + s := FindIniString( settings, section, key, ''); + if s = '' then + FindIniNumber := default + else begin + Val( s, x, ignore); + FindIniNumber := x; + Exit; + end; +end; + + +function FindIniBoolean( settings : PSection; section, key : String; default : Boolean ) : Boolean; +var s : String; +begin + s := FindIniString( settings, section, key, ''); + s := Ups( s); + if s = 'TRUE' then + FindIniBoolean := True + else if s = 'FALSE' then + FindIniBoolean := False + else + FindIniBoolean := default; +end; + + +procedure SetIniString( var settings : PSection; section, key, value : String ); +begin + SetSetting( settings, section, key, value); +end; + + +procedure SetIniNumber( var settings : PSection; section, key : String; value : LongInt ); +begin + SetSetting( settings, section, key, St( value)); +end; + + +procedure SetIniBoolean( var settings : PSection; section, key : String; value : Boolean ); +begin + if value then + SetSetting( settings, section, key, 'True') + else + SetSetting( settings, section, key, 'False'); +end; + + +function LoadIniFile( name : String; var settings : PSection ) : Boolean; +var s : String; + section : String; + p : Byte; + f : Text; +begin + LoadIniFile := False; + settings := nil; + Assign( f, name + '.INI'); + Reset( f); + if IOResult <> 0 then + Exit; + + section := ''; + while not Eof( f) do + begin + ReadLn( f, s); + if s[1] = '[' then + begin + p := Pos( ']', s); + if p > 0 then + section := Copy( s, 2, p-2) + else + section := Copy( s, 2, Length( s)-1); + end + else if (s <> '') and (s[1] <> ';') and (Pos( '=', s) > 0) then + begin + while Pos( ' ', s) > 0 do + Delete( s, Pos( ' ', s), 1); + p := Pos( '=', s); + AddSetting( settings, section, Copy( s, 1, p-1), Copy( s, p+1, Length( s)-p)); + end + else + AddSetting( settings, section, '', s); + end; + Close( f); + LoadIniFile := settings <> nil; +end; + + +procedure WriteIniFile( name : String; settings : PSection ); +var f : Text; + v : PValue; +begin + Assign( f, name + '.INI'); + Rewrite( f); + if IOResult <> 0 then + Exit; + + while settings <> nil do + begin + if settings^.name <> '' then + WriteLn( f, '[', settings^.name, ']'); + v := settings^.settings; + while v <> nil do + begin + if v^.key = '' then + WriteLn( f, v^.value) + else + WriteLn( f, v^.key, '=', v^.value); + v := v^.next; + end; + settings := settings^.next; + end; + Close( f); +end; + + +end. diff --git a/joystick.pas b/joystick.pas new file mode 100644 index 0000000..a95f0ef --- /dev/null +++ b/joystick.pas @@ -0,0 +1,106 @@ +unit Joystick; + + +interface + + +type + + JoyRec = + record + present : Boolean; + x, y : Word; + button1, + button2, + button3, + button4 : Boolean; + end; + +var + + gSticks : array [1..2] of JoyRec; + + +procedure PollSticks; +procedure InitSticks; + + +implementation + + +const + + cVGAPort = $03DA; + cStickPort = $0201; + + +procedure HSync; +begin + while Port[ cVGAPort] and 1 = 0 do; + while Port[ cVGAPort] and 1 <> 0 do; +end; + + +procedure PollSticks; +var b : Byte; + mask : Byte; + laps : Word; +begin + mask := 0; + if gSticks[1].present then + mask := 3; + if gSticks[2].present then + mask := mask or 12; + FillChar( gSticks, SizeOf( gSticks), 0); + laps := 0; + + asm cli end; + + {HSync;} + Port[ cStickPort] := $FF; { Write anything to trigger countdown } + repeat + {HSync;} + b := Port[ cStickPort]; + if b and 1 <> 0 then + Inc( gSticks[1].x); + if b and 2 <> 0 then + Inc( gSticks[1].y); + if b and 4 <> 0 then + Inc( gSticks[2].x); + if b and 8 <> 0 then + Inc( gSticks[2].y); + Inc( laps); + until (b and mask = 0) or (laps > 60000); + + asm sti end; + + gSticks[1].present := b and 3 = 0; + gSticks[2].present := b and 12 = 0; + + if gSticks[1].present then + begin + gSticks[1].button1 := b and 16 = 0; + gSticks[1].button2 := b and 32 = 0; + gSticks[1].button3 := b and 64 = 0; + gSticks[1].button4 := b and 128 = 0; + end; + + if gSticks[2].present then + begin + gSticks[2].button1 := b and 64 = 0; + gSticks[2].button2 := b and 128 = 0; + gSticks[2].button3 := b and 16 = 0; + gSticks[2].button4 := b and 32 = 0; + end; +end; + + +procedure InitSticks; +begin + gSticks[1].present := True; + gSticks[2].present := True; + PollSticks; +end; + + +end. diff --git a/keyboard.pas b/keyboard.pas new file mode 100644 index 0000000..2d0c6f3 --- /dev/null +++ b/keyboard.pas @@ -0,0 +1,252 @@ +unit Keyboard; + + +interface + + uses Dos; + + +const + + keySysReq = $54; + keyCapsLock = $3A; + keyNumLock = $45; + keyScrollLock = $46; + keyLeftCtrl = $1D; + keyLeftAlt = $38; + keyLeftShift = $2A; + keyRightCtrl = $9D; + keyAltGr = $B8; + keyRightShift = $36; + keyEsc = $01; + keyBackspace = $0E; + keyEnter = $1C; + keySpace = $39; + keyTab = $0F; + keyF1 = $3B; + keyF2 = $3C; + keyF3 = $3D; + keyF4 = $3E; + keyF5 = $3F; + keyF6 = $40; + keyF7 = $41; + keyF8 = $42; + keyF9 = $43; + keyF10 = $44; + keyF11 = $57; + keyF12 = $58; + keyA = $1E; + keyB = $30; + keyC = $2E; + keyD = $20; + keyE = $12; + keyF = $21; + keyG = $22; + keyH = $23; + keyJ = $24; + keyK = $25; + keyL = $26; + keyM = $32; + keyN = $31; + keyO = $18; + keyP = $19; + keyQ = $10; + keyR = $13; + keyS = $1F; + keyT = $14; + keyU = $16; + keyV = $2F; + keyW = $11; + keyX = $2D; + keyY = $15; + keyZ = $2C; + key1 = $02; + key2 = $03; + key3 = $04; + key4 = $05; + key5 = $06; + key6 = $07; + key7 = $08; + key8 = $09; + key9 = $0A; + key0 = $0B; + keyMinus = $0C; + keyEqual = $0D; + keyLBracket = $1A; + keyRBracket = $1B; + keySemicolon = $27; + keyTick = $28; + keyApostrophe = $29; + keyBackslash = $2B; + keyComma = $33; + keyPeriod = $34; + keySlash = $35; + keyInsert = $D2; + keyDelete = $D3; + keyHome = $C7; + keyEnd = $CF; + keyPageUp = $C9; + keyArrowLeft = $CB; + keyArrowRight = $CD; + keyArrowUp = $C8; + keyArrowDown = $D0; + keyKeypad0 = $52; + keyKeypad1 = $4F; + keyKeypad2 = $50; + keyKeypad3 = $51; + keyKeypad4 = $4B; + keyKeypad5 = $4C; + keyKeypad6 = $4D; + keyKeypad7 = $47; + keyKeypad8 = $48; + keyKeypad9 = $49; + keyKeypadComma = $53; + keyKeypadStar = $37; + keyKeypadMinus = $4A; + keyKeypadPlus = $4E; + keyKeypadEnter = $9C; + keyCtrlPrtScr = $B7; + keyShiftPrtScr = $B7; + keyKeypadSlash = $B5; + + keyNames : array [0..255] of PChar = + ( { $00 } nil, 'Esc', '1', '2', '3', '4', '5', '6', + { $08 } '7', '8', '9', '0', '+', 'Apostrophe', 'Backspace', 'Tab', + { $10 } 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + { $18 } 'O', 'P', '', '?', 'Enter', 'Left Ctrl', 'A', 'S', + { $20 } 'D', 'F', 'G', 'H', 'J', 'K', 'L', '™', + { $28 } 'Ž', '''', 'Left shift', '<', 'Z', 'X', 'C', 'V', + { $30 } 'B', 'N', 'M', ',', '.', '-', 'Right shift', '* (pad)', + { $38 } 'Alt', 'Space', 'Caps Lock', 'F1', 'F2', 'F3', 'F4', 'F5', + { $40 } 'F6', 'F7', 'F8', 'F9', 'F10', 'Num Lock', 'Scroll Lock', '7 (pad)', + { $48 } '8 (pad)', '9 (pad)', '- (pad)', '4 (pad)', '5 (pad)', '6 (pad)', '+ (pad)', '1 (pad)', + { $50 } '2 (pad)', '3 (pad)', '0 (pad)', ', (pad)', 'SysRq', nil, nil, 'F11', 'F12', + { $59 } nil, nil, nil, nil, nil, nil, nil, + { $60 } nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + { $70 } nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + { $80 } nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + { $90 } nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 'Enter (pad)', 'Right Ctrl', nil, nil, + { $A0 } nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + { $B0 } nil, nil, nil, nil, nil, '/ (pad)', nil, 'PrtScr', 'Alt Gr', nil, nil, nil, nil, nil, nil, nil, + { $C0 } nil, nil, nil, nil, nil, nil, nil, 'Home', + { $C8 } 'Up arrow', 'Page Up', nil, 'Left arrow', nil, 'Right arrow', nil, 'End', + { $D0 } 'Down arrow', nil, 'Insert', 'Delete', nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + { $E0 } nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + { $F0 } nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil + ); + + +procedure InstallKbdHandler; +procedure RemoveKbdHandler; +function KeyDown( b : byte ) : Boolean; +function AnyKeyDown : Boolean; +function GetKeyDown : Byte; +procedure ClearKeys; + + +implementation + + +var + + uOldInt9 : Pointer; { saves location of old OldInt9 vector } + uKeys : array [0..255] of Boolean; { array that holds key values } + e0Flag : Byte; + uExitProc : Pointer; + + +{$F+} +procedure NewInt9; interrupt; assembler; +asm + cli + in al, $60 { get scan code from keyboard port } + cmp al, $E0 { al = $E0 key ? } + jne @@SetScanCode + mov [e0Flag], 128 + mov al, 20h { Send 'generic' EOI to PIC } + out 20h, al + jmp @@exit +@@SetScanCode: + mov bl, al { Save scancode in BL } + and bl, 01111111b + add bl, [e0Flag] + xor bh, bh + and al, 10000000b { keep break bit, if set } + xor al, 10000000b { flip bit, 1 means pressed, 0 no } + rol al, 1 { move breakbit to bit 0 } + mov [offset uKeys + bx], al + mov [e0Flag], 0 + mov al, 20h { send EOI to PIC } + out 20h, al +@@exit: + sti +end; +{$F-} + + +procedure InstallKbdHandler; +begin + GetIntVec( $09, uOldInt9); { save old location of INT 09 handler } + SetIntVec( $09, Addr( NewInt9)); { point to new routine } + FillChar( uKeys, SizeOf( uKeys), 0); { clear the keys array } +end; + + +procedure RemoveKbdHandler; +begin + SetIntVec( $09, uOldInt9); { point back to original routine } + uOldInt9 := nil; +end; + + +function KeyDown( b : byte ) : Boolean; +begin + KeyDown := uKeys[b]; +end; + + +function AnyKeyDown : Boolean; +var b : Integer; +begin + AnyKeyDown := True; + for b := 0 to 255 do + if uKeys[b] and (keyNames[b] <> nil) then + Exit; + AnyKeyDown := False; +end; + + +function GetKeyDown : Byte; +var b : Integer; +begin + GetKeyDown := 0; + for b := 1 to 255 do + if uKeys[b] and (keyNames[b] <> nil) then + begin + GetKeyDown := b; + Exit; + end; +end; + + +procedure ClearKeys; +begin + FillChar( uKeys, SizeOf( uKeys), 0); { clear the keys array } +end; + + +{$F+} +procedure CleanUp; +begin + ExitProc := uExitProc; + if uOldInt9 <> nil then + RemoveKbdHandler; +end; +{$F-} + + +begin + uExitProc := ExitProc; + ExitProc := @CleanUp; + uOldInt9 := nil; +end. diff --git a/mainmenu.pas b/mainmenu.pas new file mode 100644 index 0000000..784afc5 --- /dev/null +++ b/mainmenu.pas @@ -0,0 +1,716 @@ +unit MainMenu; + + +interface + + +function MainScreen : Boolean; + + +implementation + + uses Strings, SPX_VGA, SPX_Txt, SPX_Fnc, + Keyboard, Globals, Pics, Fancy, BigFont, Screen, + Joystick, Stick, Equip, Buffer, Sounds, Credits; + + + +procedure Options; + +const + + max = 5; + + +var + + choice : Integer; + cmd : Byte; + done : Boolean; + + + procedure ShowOption( y, index : Integer; s : String ); + begin + if index = choice then + ftPut( 75, y, gPics[ cHilitedButton]^, True) + else + ftPut( 75, y, gPics[ cButton]^, True); + DrawLetter( 100, y, cWhite, cBlack, s); + end; + + procedure OptionsFrame; + begin + FillScreen( cSteelPlate); + BigText( 60, 20, 'OPTIONS'); + DrawLetter( 40, 175, cWhite, cBlack, 'Use left/right to highlight, Enter/button toggles'); + + ShowOption( 50, 0, 'Return to main menu'); + + case gSplitscreenMode of + + cSplitScreenNever: + ShowOption( 70, 1, 'No splitscreen'); + + cSplitScreenAlways: + ShowOption( 70, 1, 'Splitscreen always'); + + cSplitScreenOften: + ShowOption( 70, 1, 'Splitscreen often'); + + cSplitScreenSeldom: + ShowOption( 70, 1, 'Splitscreen seldom'); + + end; + + if gMusic and gSoundFX then + ShowOption( 90, 2, 'Music and Sound FX') + else if gMusic then + ShowOption( 90, 2, 'Music') + else if gSoundFX then + ShowOption( 90, 2, 'Sound FX') + else + ShowOption( 90, 2, 'No sound'); + + if gFreeMovement then + ShowOption( 110, 3, 'Move while firing always') + else + ShowOption( 110, 3, 'Move while firing only if strafing'); + + case gCampaign of + 0: ShowOption( 130, 4, 'Campaign: Zero'); + 17: ShowOption( 130, 4, 'Campaign: Seventeen'); + 4711: ShowOption( 130, 4, 'Campaign: 4711'); + 57: ShowOption( 130, 4, 'Campaign: 57!!'); + else + ShowOption( 130, 4, 'Campaign: '+St(gCampaign)); + end; + + if g2PlayerPool and (cPoolLives or cPoolCash) = (cPoolLives or cPoolCash) then + ShowOption( 150, 5, '2 player: Pool lives + cash') + else if g2PlayerPool and cPoolLives <> 0 then + ShowOption( 150, 5, '2 player: Pool lives') + else if g2PlayerPool and cPoolCash <> 0 then + ShowOption( 150, 5, '2 player: Pool cash') + else + ShowOption( 150, 5, '2 player: Every dog for itself'); + + PCopy( 2, 1); + end; + +begin + FadeOut( 5, gPalette); + SetPageActive( 2); + OptionsFrame; + FadeIn( 20, gPalette); + + done := False; + choice := 0; + while not done do + begin + if cmd <> GetMenuCommand then + begin + cmd := GetMenuCommand; + case cmd of + + cCommandShoot: + begin + case choice of + 0: done := True; + 1: gSplitScreenMode := (gSplitScreenMode + 1) mod 4; + 2: begin + if gMusic then + gMusic := False + else if gSoundFX then + begin + gSoundFX := False; + gMusic := True; + end + else begin + gSoundFX := True; + gMusic := True; + end; + StopMusic; + PlayMusic( gMenuSong); + end; + 3: gFreeMovement := not gFreeMovement; + 4: if gCampaign = 0 then + gCampaign := 17 + else if gCampaign = 17 then + gCampaign := 4711 + else if gCampaign = 4711 then + gCampaign := 57 + else + gCampaign := 0; + 5: if g2PlayerPool = cPoolLives or cPoolCash then + g2PlayerPool := 0 + else + Inc( g2PlayerPool); + end; + PlaySound( cBigGunSound); + end; + + cCommandUp: + if choice > 0 then + Dec( choice) + else + choice := max; + + cCommandDown: + if choice < max then + Inc( choice) + else + choice := 0; + + end; + OptionsFrame; + end; + end; + FadeOut( 5, gPalette); +end; + + +procedure SelectHero( var hero : Byte; otherHero : Byte ); +var delay : Integer; + faceDir : array [0..cMaxHeros] of Byte; + dir : array [0..cMaxHeros] of Byte; + gunPos : array [0..cMaxHeros] of Byte; + + procedure SelectFrame; + var x, y : Integer; + h : Byte; + begin + if delay <= 0 then + begin + for h := 0 to cMaxHeros do + begin + dir[ h] := Random( 3) + 3; + faceDir[ h] := dir[ h] + Random( 3) - 1; + if Random( 4) < 2 then + gunPos[ h] := cGunIdle + else + gunPos[ h] := cGunReady; + end; + delay := 30; + end; + + for x := 1 to 8 do + for y := 0 to 4 do + fPut( x*32, y*24 + 40, gPics[ cBkgPics[ 1, 0]]^, False); + x := (320 - 40 * (cMaxHeros+1)) div 2; + for h := 0 to cMaxHeros do + begin + if hero = h then + ftPut( x + h*40 + 10, 60, gPics[ cHilitedButton]^, True) + else if h <> otherHero then + ftPut( x + h*40 + 10, 60, gPics[ cButton]^, True); + with cHeroInfo[ h] do + begin + if h <> otherHero then + begin + DrawCharacter( x + h*40, 80, dir[ h], faceDir[ h], face, body, cGunDogs, 0, 0, cGunIdle); + DrawLetter( x + h*40, 110, cWhite, cBlack, name); + end + else begin + DrawCharacter( x + h*40, 80, cDirectionUp, cDirectionUp, face, body, cGunDogs, 0, 0, cGunIdle); + DrawLetter( x + h*40, 110, cRed, cBlack, name); + end; + end; + end; + + Dec( delay); + VSinc; + PCopy( 2, 1); + end; + +var h : Byte; + cmd : Byte; +begin + delay := 0; + FadeOut( 5, gPalette); + SetPageActive( 2); + FillScreen( cSteelPlate); + BigText( 60, 20, 'HEROES'); + DrawLetter( 40, 175, cWhite, cBlack, 'Use left/right to highlight, Enter/button to select'); + SelectFrame; + FadeIn( 5, gPalette); + + repeat + SelectFrame; + if cmd <> GetMenuCommand then + begin + cmd := GetMenuCommand; + if (cmd and cCommandLeft <> 0) and (hero > 0) and ((otherHero > 0) or (hero > 1)) then + begin + Dec( hero); + if hero = otherHero then + Dec( hero) + end; + if (cmd = cCommandRight) and (hero < cMaxHeros) and ((otherHero <> cMaxHeros) or (hero < cMaxHeros-1)) then + begin + Inc( hero); + if hero = otherHero then + Inc( hero); + end; + end; + until cmd and cCommandShoot <> 0; + PlaySound( cSwitchSound); + FadeOut( 5, gPalette); +end; + + +procedure DefineKeys( index : Integer; var keys : KeyControls ); +var delay : Integer; + faceDir : Integer; + + procedure ControlFrame; + begin + SetPageActive( 2); + fPut( 0, 60, gPics[ cSteelPlate]^, False); + fPut( 32, 60, gPics[ cSteelPlate]^, False); + fPut( 0, 80, gPics[ cSteelPlate]^, False); + fPut( 32, 80, gPics[ cSteelPlate]^, False); + with cHeroInfo[ gPlayerData[ index].hero] do + begin + DrawCharacter( 25, 65, cDirectionDownLeft, faceDir, face, body, cGunDogs, cFaceNormal, 0, cGunIdle); + PutLetter( 20, 88, cWhite, name); + end; + VSinc; + CopyRect( 0, 60, 63, 99, pages[2]^, pages[1]^); + SetPageActive( 1); + Dec( delay); + if delay < 0 then + begin + faceDir := Random( 3) + 3; + delay := 25; + end; + end; + + procedure ShowKey( y : Integer; key : Byte; color : Byte ); + begin + Bar( 170, y, 255, y+9, cBlack); + if keyNames[ key] <> nil then + PutLetter( 170, y, color, StrPas( keyNames[ key])) + else + PutLetter( 170, y, color, 'Strange?!?'); + end; + + procedure GetKey( y : Integer; var key : Byte ); + begin + ShowKey( y, key, cYellow); + repeat + key := GetKeyDown; + ControlFrame; + until not (key in [0, keyEsc]); + ShowKey( y, key, cRed); + PlaySound( cBigGunSound); + while AnyKeyDown do + ControlFrame; + end; + + procedure ClearDisplay; + begin + Bar( 64, 80, 255, 179, cBlack); + end; + + procedure ReleaseButton( y : Byte ); + begin + PutLetter( 70, y, cBrightRed, 'Release the button, please'); + while StickAnyButton( keys.stick) do + begin + VSinc; + PollSticks; + ControlFrame; + end; + Bar( 64, y, 255, y + 9, cBlack); + end; + + procedure CalibrateStick( var stick : Byte ); + + procedure WaitForStick; + begin + while not StickAnyButton( stick) do + begin + VSinc; + PollSticks; + ControlFrame; + end; + PlaySound( cBigGunSound); + end; + + var xCenter, yCenter, + xMin, yMin, + xMax, yMax : Integer; + begin + Bar( 64, 100, 255, 179, cBlack); + + ReleaseButton( 100); + + PutLetter( 70, 100, cBrightRed, 'Move stick top left and press any button'); + WaitForStick; + xMin := gSticks[ stick].x; + yMin := gSticks[ stick].y; + Bar( 64, 100, 255, 109, cBlack); + PutLetter( 70, 100, cRed, '[ '+St( xMin)+', '+St( yMin)+']'); + ReleaseButton( 110); + + PutLetter( 70, 110, cBrightRed, 'Move stick bottom right and press any button'); + WaitForStick; + xMax := gSticks[ stick].x; + yMax := gSticks[ stick].y; + Bar( 64, 110, 255, 119, cBlack); + PutLetter( 70, 110, cRed, '[ '+St( xMax)+', '+St( yMax)+']'); + ReleaseButton( 120); + + PutLetter( 70, 120, cBrightRed, 'Center stick and press any button'); + WaitForStick; + xCenter := gSticks[ stick].x; + yCenter := gSticks[ stick].y; + Bar( 64, 120, 255, 129, cBlack); + PutLetter( 70, 120, cRed, '[ '+St( xCenter)+', '+St( yCenter)+']'); + ReleaseButton( 130); + + if (xMin < xCenter) and (xCenter < xMax) and + (yMin < yCenter) and (yCenter < yMax) then + begin + gStickLeft[ stick] := (xMin + xCenter) div 2; + gStickRight[ stick] := (xCenter + xMax) div 2; + gStickUp[ stick] := (yMin + yCenter) div 2; + gStickDown[ stick] := (yCenter + yMax) div 2; + PutLetter( 70, 130, cRed, 'Calibration complete'); + end + else begin + PutLetter( 70, 130, cBrightRed, 'Error calibrating stick!'); + PutLetter( 70, 140, cRed, 'Press any key'); + stick := 0; + while not AnyKeyDown do; + end; + end; + +begin + FadeOut( 5, gPalette); + SetPageActive( 1); + FillScreen( cSteelPlate); + ClearDisplay; + + BigText( 20, 25, 'CONTROLS'); + + FadeIn( 5, gPalette); + delay := 25; + faceDir := 4; + + keys.stick := 0; + InitSticks; + if gSticks[1].present or gSticks[2].present then + begin + PutLetter( 70, 90, cRed, 'Press button for joystick or hit any key'); + repeat + ControlFrame; + VSinc; + PollSticks; + if StickAnyButton(1) then + keys.stick := 1; + if StickAnyButton(2) then + keys.stick := 2; + until AnyKeyDown or (keys.stick > 0); + end; + while AnyKeyDown do + ControlFrame; + + if keys.stick > 0 then + begin + PlaySound( cBigGunSound); + ClearDisplay; + PutLetter( 70, 90, cRed, 'Joystick ' + St( keys.stick) + ' selected'); + ReleaseButton( 100); + PutLetter( 70, 100, cRed, 'Press button 2 to calibrate stick'); + PutLetter( 70, 110, cRed, 'Press button 1 to exit'); + repeat + VSinc; + PollSticks; + until StickAnyButton( keys.stick); + PlaySound( cBigGunSound); + if StickButton2( keys.stick) then + CalibrateStick( keys.stick); + + ClearDisplay; + PutLetter( 70, 160, cRed, 'Esc is reserved'); + PutLetter( 70, 90, cRed, 'Show map'); + GetKey( 90, gMapKey); + end + else begin + keys.rotate := False; + + ClearDisplay; + PutLetter( 70, 160, cRed, 'Esc is reserved'); + + if keys.rotate then + begin + PutLetter( 70, 90, cRed, 'CounterClockwise'); + PutLetter( 70, 100, cRed, 'Clockwise'); + PutLetter( 70, 110, cRed, 'Forward'); + PutLetter( 70, 120, cRed, 'Backward'); + end + else begin + PutLetter( 70, 90, cRed, 'Left'); + PutLetter( 70, 100, cRed, 'Right'); + PutLetter( 70, 110, cRed, 'Up'); + PutLetter( 70, 120, cRed, 'Down'); + end; + PutLetter( 70, 130, cRed, 'Fire'); + PutLetter( 70, 140, cRed, 'Change weapon'); + + PutLetter( 70, 150, cRed, 'Show map'); + + ShowKey( 90, keys.left, cDarkRed); + ShowKey( 100, keys.right, cDarkRed); + ShowKey( 110, keys.up, cDarkRed); + ShowKey( 120, keys.down, cDarkRed); + ShowKey( 130, keys.shoot, cDarkRed); + ShowKey( 140, keys.switch, cDarkRed); + ShowKey( 150, gMapKey, cDarkRed); + + GetKey( 90, keys.left); + GetKey( 100, keys.right); + GetKey( 110, keys.up); + GetKey( 120, keys.down); + GetKey( 130, keys.shoot); + GetKey( 140, keys.switch); + GetKey( 150, gMapKey); + end; + + + FadeOut( 5, gPalette); +end; + + +function MainScreen : Boolean; + +const + + cTitle = 'CYBERDOGS'; + cxPlayer1 = 10; + cyPlayer1 = 70; + cxPlayer2 = 295; + cyPlayer2 = 70; + cyTitle = 20; + cyText = 50; + +var + + xTitle, + xText : Integer; + cmd : Byte; + choice, + faceDir1, + faceDir2, + delay : Integer; + done : Boolean; + + procedure MainScreenIntro; + var i : Integer; + dx, dy : Integer; + laps : Integer; + y : Integer; + begin + SetPageActive( 1); + FillScreen( cSteelPlate); + SetPageActive( 2); + FadeIn( 20, gPalette); + + { Have title appear } + dx := 10; + dy := 10; + laps := 10 + Length( cTitle); + while (laps > 0) and not AnyKeyDown do + begin + FillScreen( cSteelPlate); + FancyBigText( xTitle, cyTitle, cTitle, dx, dy, 1, 1); + VSinc; + PCopy( 2, 1); + Dec( dx); + Dec( dy); + Dec( laps); + end; + end; + + procedure ShowOption( x, y, index : Integer; s : String ); + begin + if index = choice then + ftPut( x, y, gPics[ cHilitedButton]^, True) + else + ftPut( x, y, gPics[ cButton]^, True); + DrawLetter( x + 25, y, cWhite, cBlack, s); + end; + + procedure MenuFrame; + begin + FillScreen( cSteelPlate); + BigText( xTitle, cyTitle, cTitle); + if gPlayerData[0].playing then + with cHeroInfo[ gPlayerData[0].hero] do + DrawCharacter( cxPlayer1, cyPlayer1, + cDirectionDown, faceDir1, + face, body, cGunDogs, 0, 0, cGunIdle); + if gPlayerData[1].playing then + with cHeroInfo[ gPlayerData[1].hero] do + DrawCharacter( cxPlayer2, cyPlayer2, + cDirectionDown, faceDir2, + face, body, cGunDogs, 0, 0, cGunIdle); + + if gPlayerData[0].playing then + ShowOption( 50, 75, 0, 'Player #1 is '+cHeroInfo[ gPlayerData[0].hero].name) + else + ShowOption( 50, 75, 0, 'Player #1 not playing'); + ShowOption( 50, 100, 2, 'Set controls'); + + if gPlayerData[1].playing then + ShowOption( 175, 75, 1, 'Player #2 is '+cHeroInfo[ gPlayerData[1].hero].name) + else + ShowOption( 175, 75, 1, 'Player #2 not playing'); + ShowOption( 175, 100, 3, 'Set controls'); + + ShowOption( 50, 125, 4, 'Start mission'); + ShowOption( 175, 125, 5, 'Credits'); + ShowOption( 50, 150, 6, 'Options screen'); + ShowOption( 175, 150, 7, 'Quit'); + + DrawLetter( 30, 185, cWhite, cBlack, 'Use up/down/left/right to highlight, Enter/button to select'); + end; + + procedure UpdateMenuFrame; + var textPos : Integer; + begin + Inc( delay); + if delay > 15 then + begin + faceDir1 := Random( 3) + 3; + faceDir2 := Random( 3) + 3; + delay := 0; + end; + end; + + procedure Redraw; + begin + SetPageActive( 2); + MenuFrame; + PCopy( 2, 1); + FadeIn( 20, gPalette); + end; + +begin + xTitle := (320 - Length( cTitle) * CharWidth) div 2; + MainScreenIntro; + + delay := 0; + faceDir1 := cDirectionDownRight; + faceDir2 := cDirectionDownLeft; + + choice := 0; + done := False; + repeat + MenuFrame; + VSinc; + VSinc; + PCopy( 2, 1); + UpdateMenuFrame; + + if cmd <> GetMenuCommand then + begin + cmd := GetMenuCommand; + case cmd of + + cCommandShoot: + begin + PlaySound( cBigGunSound); + case choice of + + 0: + begin + gPlayerData[0].playing := not gPlayerData[0].playing; + if gPlayerData[0].playing then + begin + if gPlayerData[1].playing then + SelectHero( gPlayerData[0].hero, gPlayerData[1].hero) + else + SelectHero( gPlayerData[0].hero, $FF); + Redraw; + end; + end; + + 1: + begin + gPlayerData[1].playing := not gPlayerData[1].playing; + if gPlayerData[1].playing then + begin + if gPlayerData[0].playing then + SelectHero( gPlayerData[1].hero, gPlayerData[0].hero) + else + SelectHero( gPlayerData[1].hero, $FF); + Redraw; + end; + end; + + 2: + begin + DefineKeys( 0, hero1Keys); + gPlayerData[0].playing := True; + Redraw; + end; + + 3: + begin + DefineKeys( 1, hero2Keys); + gPlayerData[1].playing := True; + Redraw; + end; + + 4: if gPlayerData[0].playing or gPlayerData[1].playing then + done := True; + + 7: done := True; + + 6: + begin + Options; + Redraw; + end; + + 5: + begin + DisplayCredits; + Redraw; + end; + end; + end; + + cCommandUp: + if choice > 1 then + Dec( choice, 2); + + cCommandDown: + if choice <= 5 then + Inc( choice, 2); + + cCommandLeft: + if choice > 0 then + Dec( choice); + + cCommandRight: + if choice < 7 then + Inc( choice); + + cCommandFreeze: + begin + choice := 7; + done := True; + end; + + end; + end; + until done; + FadeOut( 20, gPalette); + MainScreen := (choice = 4); +end; + + +end. diff --git a/pics.pas b/pics.pas new file mode 100644 index 0000000..c53a43d --- /dev/null +++ b/pics.pas @@ -0,0 +1,677 @@ +unit Pics; + + +interface + + uses SPX_VGA, + Globals, GameArea; + + +const + + cMaxPics = 351; + + +var + + gPics : array [0..cMaxPics] of Pointer; + gBkgPics : array [0..cBkgPicMax] of Pointer; + + +const + + cBodyPics : array [0..cMaxBody, 0..7, 0..4] of Integer = + ( + ( + ( 10, 14, 13, 12, 11 ), + ( 10, 14, 13, 12, 11 ), + ( 15, 16, 17, 18, 19 ), + ( 0, 4, 3, 2, 1 ), + ( 0, 4, 3, 2, 1 ), + ( 0, 4, 3, 2, 1 ), + ( 5, 9, 8, 7, 6 ), + ( 5, 9, 8, 7, 6 ) + ), + ( + ( 30, 34, 33, 32, 31 ), + ( 30, 34, 33, 32, 31 ), + ( 35, 36, 37, 38, 39 ), + ( 20, 24, 23, 22, 21 ), + ( 20, 24, 23, 22, 21 ), + ( 20, 24, 23, 22, 21 ), + ( 25, 29, 28, 27, 26 ), + ( 25, 29, 28, 27, 26 ) + ), + ( + ( 246, 250, 249, 248, 247 ), + ( 246, 250, 249, 248, 247 ), + ( 251, 252, 253, 254, 255 ), + ( 236, 240, 239, 238, 237 ), + ( 236, 240, 239, 238, 237 ), + ( 236, 240, 239, 238, 237 ), + ( 241, 245, 244, 243, 242 ), + ( 241, 245, 244, 243, 242 ) + ), + ( + ( 266, 270, 269, 268, 267 ), + ( 266, 270, 269, 268, 267 ), + ( 271, 272, 273, 274, 275 ), + ( 256, 260, 259, 258, 257 ), + ( 256, 260, 259, 258, 257 ), + ( 256, 260, 259, 258, 257 ), + ( 261, 265, 264, 263, 262 ), + ( 261, 265, 264, 263, 262 ) + ), + ( + ( 334, 338, 337, 336, 335 ), + ( 334, 338, 337, 336, 335 ), + ( 339, 340, 341, 342, 343 ), + ( 324, 328, 327, 326, 325 ), + ( 324, 328, 327, 326, 325 ), + ( 324, 328, 327, 326, 325 ), + ( 329, 333, 332, 331, 330 ), + ( 329, 333, 332, 331, 330 ) + ) + ); + + cFaceNormal = 0; + cFaceHard = 1; + + cFacePics : array [0..cMaxFace, 0..7, 0..1] of Integer = + ( + ( ( 64, 64 ), ( 63, 63 ), ( 62, 197 ), ( 61, 196 ), ( 60, 195 ), ( 59, 194 ), ( 58, 193 ), ( 65, 65 ) ), + ( ( 72, 72 ), ( 71, 71 ), ( 70, 202 ), ( 69, 201 ), ( 68, 200 ), ( 67, 199 ), ( 66, 198 ), ( 73, 73 ) ), + ( ( 80, 80 ), ( 79, 79 ), ( 78, 207 ), ( 77, 206 ), ( 76, 205 ), ( 75, 204 ), ( 74, 203 ), ( 81, 81 ) ), + ( ( 88, 88 ), ( 87, 87 ), ( 86, 86 ), ( 85, 85 ), ( 84, 84 ), ( 83, 83 ), ( 82, 82 ), ( 89, 89 ) ), + ( ( 226, 226 ), ( 225, 225 ), ( 224, 224 ), ( 223, 223 ), ( 222, 222 ), ( 221, 221 ), ( 220, 220 ), ( 227, 227 ) ), + ( ( 234, 234 ), ( 233, 233 ), ( 232, 232 ), ( 231, 231 ), ( 230, 230 ), ( 229, 229 ), ( 228, 228 ), ( 235, 235 ) ), + ( ( 285, 285 ), ( 284, 284 ), ( 283, 283 ), ( 282, 282 ), ( 281, 281 ), ( 280, 280 ), ( 279, 279 ), ( 286, 286 ) ), + ( ( 297, 297 ), ( 296, 296 ), ( 295, 295 ), ( 294, 294 ), ( 293, 293 ), ( 292, 292 ), ( 291, 291 ), ( 298, 298 ) ) + ); + + + cGunIdle = 0; + cGunReady = 1; + cGunRecoil = 2; + cMaxGunPosition = 2; + + cGunPics : array [0..cGunMax, 0..7, 0..cMaxGunPosition] of Integer = + ( + ( + ( 50, 51, 51 ), + ( 52, 53, 53 ), + ( 54, 55, 55 ), + ( 42, 57, 57 ), + ( 42, 43, 43 ), + ( 44, 45, 45 ), + ( 46, 47, 47 ), + ( 48, 49, 49 ) + ) + ); + + cMaxDeath = 9; + cDeathPics : array [1..cMaxDeath] of Integer = + ( 184, 185, 186, 187, 188, 189, 190, 191, 192 ); + + cMaxFireBalls = 5; + cExplosionPics : array [0..cMaxFireBalls] of Integer = + ( 90, 91, 92, 93, 94, 95 ); + + cCyanBolts = 0; + cRedBolts = 1; + cMinigunBolts = 2; + cFlamerBolts = 3; + cGrenades = 4; + cGreenBolts = 5; + cYellowBolts = 6; + cPowerBolts = 7; + cZapperBolts = 8; + cMaxBolts = 8; + + cShotConnectPic = 8; + + cShotPics : array [0..cMaxBolts, 0..8] of Integer = + ( + ( 104, 107, 105, 106, 104, 107, 105, 106, 179 ), + ( 108, 111, 109, 110, 108, 111, 109, 110, 180 ), + ( 112, 112, 112, 112, 112, 112, 112, 112, 181 ), + ( 96, 97, 98, 99, 96, 97, 98, 99, 96 ), + ( 100, 101, 102, 103, 100, 101, 102, 103, 100 ), + ( 307, 310, 308, 309, 307, 310, 308, 309, 315 ), + ( 311, 314, 312, 313, 311, 314, 312, 313, 181 ), + ( 316, 316, 316, 316, 316, 316, 316, 316, 179 ), + ( 181, 180, 181, 180, 181, 180, 181, 180, 90 ) + ); + + cWallPicMax = 0; + cWallPics : array [0..cWallPicMax, 1..15] of Integer = + ( + ( 126, 120, 125, 127, 130, 119, 129, 128, 122, 177, 123, 121, 124, 131, 132 ) + ); + + cWallPatternMax = 2; + cWallPatterns : array [0..cWallPatternMax, 0..1] of Integer = + ( ( 299, 300 ), ( 301, 302 ), ( 303, 304 ) ); + + cWallColorMax = 7; + cWallColors : array [0..cWallColorMax] of RGBType = + ( + (red: 40; green: 0; blue: 0 ), + (red: 40; green: 30; blue: 0 ), + (red: 30; green: 0; blue: 30 ), + (red: 30; green: 30; blue: 30 ), + (red: 15; green: 15; blue: 15 ), + (red: 10; green: 10; blue: 40 ), + (red: 0; green: 30; blue: 0 ), + (red: 10; green: 30; blue: 10 ) + ); + + cWallColorFirst = 96; + cMaxWallColors = 7; + + cBkgAnimatedChaosBox = 0; + cBkgAnimatedExit = 1; + cBkgAnimatedFan = 2; + cMaxBkgAnimated = 2; + cBkgAnimated : array [0..cMaxBkgAnimated, 0..3] of Integer = + ( ( 276, 277, 276, 278 ), + ( 287, 288, 289, 290 ), + ( 318, 318, 319, 319 ) ); + + cBkgMax = 3; + + cBkgPics: array [0..cBkgMax, 0..1] of Integer = + ( ( 113, 114 ), + ( 115, 116 ), + ( 117, 118 ), + ( 212, 213 ) + ); + + cBkgColorMax = 5; + cBkgColors : array [0..cBkgColorMax] of RGBType = + ( + (red: 13; green: 13; blue: 0 ), + (red: 0; green: 15; blue: 0 ), + (red: 25; green: 0; blue: 0 ), + (red: 11; green: 11; blue: 11 ), + (red: 9; green: 9; blue: 9 ), + (red: 7; green: 7; blue: 20 ) + ); + + cBkgColorFirst = 106; + cMaxBkgColors = 5; + +type + + Architecture = + record + wallPattern, + wallColor, + bkg, + bkgColor, + style : Byte; + end; + +const + + cArchitectureMax = 14; + cArchitectureStyleMax = 2; + cArchitectures : array [0..cArchitectureMax] of Architecture = + ( + ( wallPattern:2; wallColor:3; bkg:2; bkgColor:3; style:0 ), + ( wallPattern:2; wallColor:4; bkg:2; bkgColor:3; style:0 ), + ( wallPattern:2; wallColor:5; bkg:2; bkgColor:4; style:0 ), + ( wallPattern:2; wallColor:4; bkg:1; bkgColor:0; style:0 ), + ( wallPattern:2; wallColor:5; bkg:0; bkgColor:5; style:0 ), + ( wallPattern:1; wallColor:6; bkg:0; bkgColor:5; style:1 ), + ( wallPattern:1; wallColor:7; bkg:1; bkgColor:1; style:1 ), + ( wallPattern:1; wallColor:6; bkg:3; bkgColor:1; style:1 ), + ( wallPattern:1; wallColor:7; bkg:0; bkgColor:5; style:1 ), + ( wallPattern:1; wallColor:2; bkg:1; bkgColor:4; style:1 ), + ( wallPattern:0; wallColor:0; bkg:0; bkgColor:1; style:2 ), + ( wallPattern:0; wallColor:0; bkg:1; bkgColor:2; style:2 ), + ( wallPattern:0; wallColor:0; bkg:2; bkgColor:3; style:2 ), + ( wallPattern:0; wallColor:0; bkg:3; bkgColor:4; style:2 ), + ( wallPattern:0; wallColor:5; bkg:1; bkgColor:3; style:2 ) + ); + + cShadowPic = 133; + cSteelPlate = 178; + cButton = 182; + cHilitedButton = 183; + + cSkullPic = 208; + cBloodPic = 209; + cCraterPic = 210; + cGoldSkullPic = 211; + cArmorPic = 219; + cArmorAddOnPic = 305; + cAxePic = 306; + + cWallPic1 = 214; + cWallPic2 = 215; + cWallPic3 = 216; + cWallPic4 = 217; + cWallPic5 = 218; + + cExitPic = 287; + + cChaosBoxBangPic = 317; + cBlueBoxPic = 320; + cBlueBoxBangPic = 321; + cGreyBoxPic = 322; + cGreyBoxBangPic = 323; + cCratePic = 344; + cCrateBangPic = 345; + cBoxPic = 40; + cBoxBangPic = 41; + + cDocsPic = 350; + cFolderPic = 346; + cDiskPic = 347; + cCircuitPic = 348; + cTeddyPic = 349; + + +procedure KillPics; +function LoadPics( filename : String; var pics; maxPics : Integer; var p : RGBList ) : Boolean; +procedure BuildBkgPics( const a : Architecture; var p : RGBList ); +procedure SetClipRange( x, y, x2, y2 : Integer ); + + + +implementation + + uses Keyboard; + + +type + + PicArray = array [0..500] of Pointer; + + +procedure KillPics; +var i : Integer; +begin + for i := 0 to cMaxPics do + if gPics[ i] <> nil then + begin + FreeMem( gPics[i], ImageSize( gPics[i]^)); + gPics[i] := nil; + end; +end; + + +function LoadPics( filename : String; var pics; maxPics : Integer; var p : RGBList ) : Boolean; +var f : File; + i : Integer; + size : Word; +begin + LoadPics := False; + Assign( f, filename); + Reset( f, 1); + if IOResult <> 0 then + Exit; + + BlockRead( f, p, SizeOf( p)); + + i := 0; + while not Eof( f) and (i <= maxPics) do + begin + BlockRead( f, size, SizeOf( size)); + if size <> 0 then + begin + GetMem( PicArray( pics)[i], size); + if gPics[i] <> nil then + BlockRead( f, PicArray( pics)[i]^, size) + else + Seek( f, FilePos( f) + size); + end; + Inc( i); + end; + Close( f); + + LoadPics := True; +end; + + +procedure BuildBkgPics( const a : Architecture; var p : RGBList ); + + procedure GetPic( i, x, y : Integer ); + begin + fGet( x, y, x + 31, y + 23, gBkgPics[ i and cBkgFlagMask]^); + end; + + procedure DecRGB( var rgb : RGBType ); + + procedure DecRGBComponent( var x : Byte ); + begin + if x > 20 then + Dec( x, 3) + else if x > 10 then + Dec( x, 2) + else if x > 0 then + Dec( x); + end; + + begin + DecRGBComponent( rgb.red); + DecRGBComponent( rgb.green); + DecRGBComponent( rgb.blue); + end; + + procedure OverlayPic( srcLeft, srcTop, dstLeft, dstTop : Integer; color : Byte ); + var x, y : Integer; + begin + for x := 0 to 31 do + for y := 0 to 23 do + if Point( x + dstLeft, y + dstTop, 2) = color then + PSet( x + dstLeft, y + dstTop, Point( x + srcLeft, y + srcTop, 2)); + end; + + procedure DoOverlay( pic, pattern : Pointer; color : Byte ); + var x, y, w, h : Integer; + picPtr, patternPtr : PChar; + begin + ImageDims( pic^, w, h); + if (w <> 32) or (h <> 24) then + Exit; + ImageDims( pattern^, w, h); + if (w <> 32) or (h <> 24) then + Exit; + + picPtr := pic; + Inc( picPtr, 4); + patternPtr := pattern; + Inc( patternPtr, 4); + for x := 0 to 31 do + for y := 0 to 23 do + begin + if picPtr^ = Chr( color) then + picPtr^ := patternPtr^; + Inc( picPtr); + Inc( patternPtr); + end; + end; + + procedure OverlayPattern( pic, pattern : Integer ); + begin + DoOverlay( gBkgPics[ pic and cBkgFlagMask], + gPics[ cWallPatterns[ pattern, 0]], 254); + DoOverlay( gBkgPics[ pic and cBkgFlagMask], + gPics[ cWallPatterns[ pattern, 1]], 0); + end; + +var tmp : Pointer; + c : Integer; + wallPic : Integer; + rgb : RGBType; +begin + wallPic := 0; {cWallPicsIndex[ wall];} + + GetMem( tmp, BuffSize( 32, 24)); + SetPageActive(2); + Cls(0); + + fPut( 0, 0, gPics[ cBkgPics[ a.bkg, 0]]^, False); + fPut( 32, 0, gPics[ cBkgPics[ a.bkg, 1]]^, False); + fPut( 64, 0, gPics[ cBkgPics[ a.bkg, 0]]^, False); + fPut( 96, 0, gPics[ cBkgPics[ a.bkg, 0]]^, False); + fPut( 128, 0, gPics[ cBkgPics[ a.bkg, 0]]^, False); + + fPut( 192, 0, gPics[ cWallPics[ wallPic, 13]]^, False); + + {fPut( 224, 0, gPics[ cWallPatterns[ 1, 0]]^, False); + fPut( 256, 0, gPics[ cWallPatterns[ 1, 1]]^, False);} + + fGet( 32, 0, 63, 11, tmp^); + fPut( 64, 0, tmp^, False); + fGet( 32, 0, 47, 11, tmp^); + fPut( 96, 0, tmp^, False); + fGet( 32, 12, 47, 23, tmp^); + fPut( 128, 12, tmp^, False); + + fPut( 0, 24, gPics[ cWallPics[ wallPic, 1]]^, False); + fPut( 32, 24, gPics[ cWallPics[ wallPic, 2]]^, False); + fPut( 64, 24, gPics[ cWallPics[ wallPic, 3]]^, False); + fPut( 96, 24, gPics[ cWallPics[ wallPic, 4]]^, False); + fPut( 128, 24, gPics[ cWallPics[ wallPic, 5]]^, False); + fPut( 160, 24, gPics[ cWallPics[ wallPic, 6]]^, False); + fPut( 192, 24, gPics[ cWallPics[ wallPic, 7]]^, False); + fPut( 224, 24, gPics[ cWallPics[ wallPic, 8]]^, False); + fPut( 0, 48, gPics[ cWallPics[ wallPic, 9]]^, False); + fPut( 32, 48, gPics[ cWallPics[ wallPic, 10]]^, False); + fPut( 64, 48, gPics[ cWallPics[ wallPic, 11]]^, False); + fPut( 96, 48, gPics[ cWallPics[ wallPic, 12]]^, False); + fPut( 128, 48, gPics[ cWallPics[ wallPic, 13]]^, False); + fPut( 160, 48, gPics[ cWallPics[ wallPic, 14]]^, False); + fPut( 192, 48, gPics[ cWallPics[ wallPic, 15]]^, False); + { + OverlayPic( 224, 0, 0, 24, 254); OverlayPic( 256, 0, 0, 24, 0); + OverlayPic( 224, 0, 32, 24, 254); OverlayPic( 256, 0, 32, 24, 0); + OverlayPic( 224, 0, 64, 24, 254); OverlayPic( 256, 0, 64, 24, 0); + OverlayPic( 224, 0, 96, 24, 254); OverlayPic( 256, 0, 96, 24, 0); + OverlayPic( 224, 0, 128, 24, 254); OverlayPic( 256, 0, 128, 24, 0); + OverlayPic( 224, 0, 160, 24, 254); OverlayPic( 256, 0, 160, 24, 0); + OverlayPic( 224, 0, 192, 24, 254); OverlayPic( 256, 0, 192, 24, 0); + OverlayPic( 224, 0, 224, 24, 254); OverlayPic( 256, 0, 224, 24, 0); + OverlayPic( 224, 0, 0, 48, 254); OverlayPic( 256, 0, 0, 48, 0); + OverlayPic( 224, 0, 32, 48, 254); OverlayPic( 256, 0, 32, 48, 0); + OverlayPic( 224, 0, 64, 48, 254); OverlayPic( 256, 0, 64, 48, 0); + OverlayPic( 224, 0, 96, 48, 254); OverlayPic( 256, 0, 96, 48, 0); + OverlayPic( 224, 0, 128, 48, 254); OverlayPic( 256, 0, 128, 48, 0); + OverlayPic( 224, 0, 160, 48, 254); OverlayPic( 256, 0, 160, 48, 0); + OverlayPic( 224, 0, 192, 48, 254); OverlayPic( 256, 0, 192, 48, 0); + } + fGet( 48, 0, 63, 23, tmp^); + fPut( 48, 24, tmp^, False); + fPut( 176, 24, tmp^, False); + fPut( 208, 24, tmp^, False); + fPut( 16, 48, tmp^, False); + fGet( 16, 0, 31, 11, tmp^); + fPut( 48, 48, tmp^, False); + fPut( 112, 48, tmp^, False); + fGet( 48, 12, 63, 23, tmp^); + fPut( 48, 60, tmp^, False); + fPut( 112, 60, tmp^, False); + + fPut( 0, 72, gPics[ cShadowPic]^, False); + fPut( 32, 72, gPics[ cWallPics[ wallPic, 6]]^, False); + fPut( 64, 72, gPics[ cWallPics[ wallPic, 7]]^, False); + fPut( 96, 72, gPics[ cWallPics[ wallPic, 9]]^, False); + fPut( 128, 72, gPics[ cWallPics[ wallPic, 10]]^, False); + fPut( 160, 72, gPics[ cWallPics[ wallPic, 2]]^, False); + fPut( 192, 72, gPics[ cWallPics[ wallPic, 12]]^, False); + { + OverlayPic( 224, 0, 0, 72, 254); OverlayPic( 256, 0, 0, 72, 0); + OverlayPic( 224, 0, 32, 72, 254); OverlayPic( 256, 0, 32, 72, 0); + OverlayPic( 224, 0, 64, 72, 254); OverlayPic( 256, 0, 64, 72, 0); + OverlayPic( 224, 0, 96, 72, 254); OverlayPic( 256, 0, 96, 72, 0); + OverlayPic( 224, 0, 128, 72, 254); OverlayPic( 256, 0, 128, 72, 0); + OverlayPic( 224, 0, 160, 72, 254); OverlayPic( 256, 0, 160, 72, 0); + OverlayPic( 224, 0, 192, 72, 254); OverlayPic( 256, 0, 192, 72, 0); + } + fGet( 16, 72, 31, 95, tmp^); + fPut( 48, 72, tmp^, False); + fPut( 80, 72, tmp^, False); + fPut( 112, 72, tmp^, False); + fPut( 144, 72, tmp^, False); + fPut( 176, 72, tmp^, False); + fPut( 208, 72, tmp^, False); + + fPut( 0, 96, gPics[ cBkgPics[ a.bkg, 0]]^, False); + fPut( 32, 96, gPics[ cBkgPics[ a.bkg, 0]]^, False); + fPut( 64, 96, gPics[ cBkgPics[ a.bkg, 0]]^, False); + + fPut( 96, 96, gPics[ cWallPics[ wallPic, cBkgHorz and cBkgFlagMask]]^, False); + fPut( 128, 96, gPics[ cWallPics[ wallPic, cBkgHorz and cBkgFlagMask]]^, False); + fPut( 160, 96, gPics[ cWallPics[ wallPic, cBkgHorz and cBkgFlagMask]]^, False); + fPut( 192, 96, gPics[ cWallPics[ wallPic, cBkgHorz and cBkgFlagMask]]^, False); + fPut( 224, 96, gPics[ cWallPics[ wallPic, cBkgHorz and cBkgFlagMask]]^, False); + + fPut( 0, 120, gPics[ cBkgPics[ a.bkg, 0]]^, False); + fPut( 32, 120, gPics[ cBkgPics[ a.bkg, 0]]^, False); + fPut( 64, 120, gPics[ cBkgPics[ a.bkg, 0]]^, False); + fPut( 96, 120, gPics[ cBkgPics[ a.bkg, 0]]^, False); + fPut( 128, 120, gPics[ cBkgPics[ a.bkg, 0]]^, False); + fPut( 160, 120, gPics[ cBkgPics[ a.bkg, 0]]^, False); + fPut( 192, 120, gPics[ cBkgPics[ a.bkg, 0]]^, False); + fPut( 224, 120, gPics[ cBkgPics[ a.bkg, 0]]^, False); + fPut( 256, 120, gPics[ cBkgPics[ a.bkg, 0]]^, False); + + fPut( 0, 144, gPics[ cBkgPics[ a.bkg, 0]]^, False); + fPut( 32, 144, gPics[ cBkgPics[ a.bkg, 0]]^, False); + fPut( 64, 144, gPics[ cBkgPics[ a.bkg, 0]]^, False); + fPut( 96, 144, gPics[ cBkgPics[ a.bkg, 0]]^, False); + fPut( 128, 144, gPics[ cBkgPics[ a.bkg, 0]]^, False); + fPut( 160, 144, gPics[ cBkgPics[ a.bkg, 0]]^, False); + fPut( 192, 144, gPics[ cBkgPics[ a.bkg, 0]]^, False); + + fPut( 0, 168, gPics[ cBkgPics[ a.bkg, 0]]^, False); + fPut( 32, 168, gPics[ cBkgPics[ a.bkg, 0]]^, False); + fPut( 64, 168, gPics[ cBkgPics[ a.bkg, 0]]^, False); + fPut( 96, 168, gPics[ cBkgPics[ a.bkg, 0]]^, False); + fPut( 128, 168, gPics[ cBkgPics[ a.bkg, 0]]^, False); + + fTPut( 0, 96, gPics[ cSkullPic]^, False); + fTPut( 32, 96, gPics[ cBloodPic]^, False); + fTPut( 64, 96, gPics[ cCraterPic]^, False); + + fTPut( 96, 96, gPics[ cWallPic1]^, False); + fTPut( 128, 96, gPics[ cWallPic2]^, False); + fTPut( 160, 96, gPics[ cWallPic3]^, False); + fTPut( 192, 96, gPics[ cWallPic4]^, False); + fTPut( 224, 96, gPics[ cWallPic5]^, False); + { + OverlayPic( 224, 0, 96, 96, 254); OverlayPic( 256, 0, 96, 96, 0); + OverlayPic( 224, 0, 128, 96, 254); OverlayPic( 256, 0, 128, 96, 0); + OverlayPic( 224, 0, 160, 96, 254); OverlayPic( 256, 0, 160, 96, 0); + OverlayPic( 224, 0, 192, 96, 254); OverlayPic( 256, 0, 192, 96, 0); + OverlayPic( 224, 0, 224, 96, 254); OverlayPic( 256, 0, 224, 96, 0); + } + fTPut( 0, 120, gPics[ cGunStyles[ cPowerGun].pic]^, False); + fTPut( 32, 120, gPics[ cGunStyles[ cBlaster].pic]^, False); + fTPut( 64, 120, gPics[ cGunStyles[ cSprayer].pic]^, False); + fTPut( 96, 120, gPics[ cGunStyles[ cFlamer].pic]^, False); + fTPut( 128, 120, gPics[ cGunStyles[ cGrenade].pic]^, False); + fTPut( 160, 120, gPics[ cArmorPic]^, False); + fTPut( 192, 120, gPics[ cGoldSkullPic]^, False); + fTPut( 224, 120, gPics[ cArmorAddOnPic]^, False); + fTPut( 256, 120, gPics[ cAxePic]^, False); + + fTPut( 0, 144, gPics[ cBlueBoxBangPic]^, False); + fTPut( 32, 144, gPics[ cGreyBoxBangPic]^, False); + fTPut( 64, 144, gPics[ cChaosBoxBangPic]^, False); + fTPut( 96, 144, gPics[ cCratePic]^, False); + fTPut( 128, 144, gPics[ cCrateBangPic]^, False); + fTPut( 160, 144, gPics[ cBoxBangPic]^, False); + + fTPut( 0, 168, gPics[ cDocsPic]^, False); + fTPut( 32, 168, gPics[ cFolderPic]^, False); + fTPut( 64, 168, gPics[ cDiskPic]^, False); + fTPut( 96, 168, gPics[ cCircuitPic]^, False); + fTPut( 128, 168, gPics[ cTeddyPic]^, False); + + FreeMem( tmp, BuffSize( 32, 24)); + + rgb := cWallColors[ a.wallColor]; + for c := 0 to cMaxWallColors do + begin + p[ cWallColorFirst + c] := rgb; + DecRGB( rgb); + end; + rgb := cBkgColors[ a.bkgColor]; + for c := 0 to cMaxBkgColors do + begin + p[ cBkgColorFirst + c] := rgb; + DecRGB( rgb); + end; + + GetPic( 0, 0, 0); + GetPic( 16, 64, 0); + GetPic( 18, 96, 0); + GetPic( 17, 128, 0); + + GetPic( 19, 192, 0); + + GetPic( 1, 0, 24); + GetPic( 2, 32, 24); + GetPic( 3, 64, 24); + GetPic( 4, 96, 24); + GetPic( 5, 128, 24); + GetPic( 6, 160, 24); + GetPic( 7, 192, 24); + GetPic( 8, 224, 24); + GetPic( 9, 0, 48); + GetPic( 10, 32, 48); + GetPic( 11, 64, 48); + GetPic( 12, 96, 48); + GetPic( 13, 128, 48); + GetPic( 14, 160, 48); + GetPic( 15, 192, 48); + + for c := 1 to 15 do + OverlayPattern( c, a.wallPattern); + + GetPic( 20, 0, 72); + GetPic( 22, 32, 72); + GetPic( 23, 64, 72); + GetPic( 24, 96, 72); + GetPic( 25, 128, 72); + GetPic( 21, 160, 72); + GetPic( 26, 192, 72); + + for c := 20 to 26 do + OverlayPattern( c, a.wallPattern); + + GetPic( cBkgFloorSkull, 0, 96); + GetPic( cBkgFloorBlood, 32, 96); + GetPic( cBkgFloorCrater, 64, 96); + + GetPic( cBkgHorz1, 96, 96); + GetPic( cBkgHorz2, 128, 96); + GetPic( cBkgHorz3, 160, 96); + GetPic( cBkgHorz4, 192, 96); + GetPic( cBkgHorz5, 224, 96); + + OverlayPattern( cBkgHorz1, a.wallPattern); + OverlayPattern( cBkgHorz2, a.wallPattern); + OverlayPattern( cBkgHorz3, a.wallPattern); + OverlayPattern( cBkgHorz4, a.wallPattern); + OverlayPattern( cBkgHorz5, a.wallPattern); + + GetPic( cBkgFloorItem1, 0, 120); + GetPic( cBkgFloorItem2, 32, 120); + GetPic( cBkgFloorItem3, 64, 120); + GetPic( cBkgFloorItem4, 96, 120); + GetPic( cBkgFloorItem5, 128, 120); + GetPic( cBkgFloorItem6, 160, 120); + GetPic( cBkgFloorItem7, 192, 120); + GetPic( cBkgFloorArmorAddOn, 224, 120); + GetPic( cBkgFloorAxe, 256, 120); + + GetPic( cBkgBlueBoxRemains, 0, 144); + GetPic( cBkgGreyBoxRemains, 32, 144); + GetPic( cBkgChaosBoxRemains, 64, 144); + GetPic( cBkgCrate and cBkgFlagMask, 96, 144); + GetPic( cBkgCrateRemains, 128, 144); + GetPic( cBkgBoxRemains, 160, 144); + + GetPic( cBkgFloorDocs, 0, 168); + GetPic( cBkgFloorFolder, 32, 168); + GetPic( cBkgFloorDisk, 64, 168); + GetPic( cBkgFloorCircuit, 96, 168); + GetPic( cBkgFloorTeddy, 128, 168); +end; + + +procedure SetClipRange( x, y, x2, y2 : Integer ); +begin + WinMinX := x; + WinMaxX := x2; + WinMinY := y; + WinMaxY := y2; +end; + + +end. diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..598fd83 --- /dev/null +++ b/readme.txt @@ -0,0 +1,20 @@ +This is the source code for my game Cyberdogs, written ca 1994. +This source is released into the public domain. + +Note: I still retain copyright for the game and the graphics. +You may use the source as you wish, but not the graphics or the game itself. + +The source is written for Borland Pascal 7.0 and makes use of two third-party libraries: + +SPX (2.0 or 3.0, can't recall) graphics library by Scott Ramsay. + +DSMI soundlibrary by Otto Chrons and Jussi Lahdenniemi (I probably spelled that wrong). + +Neither of these are included. +They are not mine to give away and I no longer have copies of them anyway. + +I will not give any support for this source. You're on your own. + + +Nov 24th 1998, Ronny Wester +http://www.rat.se/homepages/ronny diff --git a/screen.pas b/screen.pas new file mode 100644 index 0000000..e5e9dfe --- /dev/null +++ b/screen.pas @@ -0,0 +1,560 @@ +unit Screen; + + +interface + + uses Globals, Elements, Sounds; + + +procedure Frame; +procedure DrawCharacter( x, y, d, fd, face, body, gunPic, facial, phase, gunPos : Integer ); +procedure FillScreen( pic : Integer ); +function Distance( c1, c2 : PCharacter ) : LongInt; + + +type + + OffsetRec = + record + x, y : Integer; + end; + + +const + + gVSync : Boolean = True; + + cMuzzleOfs : array [0..7] of OffsetRec = + ( ( x: 14; y: 3 ), + ( x: 21; y: 4 ), + ( x: 23; y: 11 ), + ( x: 9; y: 16 ), + ( x: 1; y: 17 ), + ( x: -5; y: 14 ), + ( x: -9; y: 8 ), + ( x: -2; y: 0 ) + ); + + cMuzzleDXY : array [0..7] of OffsetRec = + ( ( x: 0; y: -3 ), + ( x: 2; y: -2 ), + ( x: 3; y: 0 ), + ( x: 2; y: 2 ), + ( x: 0; y: 3 ), + ( x: -2; y: 2 ), + ( x: -3; y: 0 ), + ( x: -2; y: -2 ) + ); + + +implementation + + uses SPX_Txt, SPX_Fnc, Pics, Buffer, Fancy, GameArea, + SPX_VGA, KeyBoard, Joystick, Stick; + + +const + + cHeadOfs : array [0..7] of OffsetRec = + ( ( x: 3; y: 0 ), + ( x: 3; y: 0 ), + ( x: 3; y: 0 ), + ( x: 3; y: 0 ), + ( x: 3; y: 0 ), + ( x: 3; y: 0 ), + ( x: 3; y: 0 ), + ( x: 3; y: 0 ) + ); + + cBodyOfs : array [0..7] of OffsetRec = + ( ( x: 0; y: 7 ), + ( x: 0; y: 7 ), + ( x: 3; y: 7 ), + ( x: 0; y: 7 ), + ( x: 0; y: 7 ), + ( x: 0; y: 7 ), + ( x: 2; y: 7 ), + ( x: 2; y: 7 ) + ); + + cGunOfs : array [0..7, 0..cMaxGunPosition] of OffsetRec = + ( ( ( x: 12; y: 1 ), ( x: 12; y: 3 ), ( x: 12; y: 4 ) ), + ( ( x: 11; y: 3 ), ( x: 11; y: 4 ), ( x: 10; y: 5 ) ), + ( ( x: 10; y: 1 ), ( x: 10; y: 8 ), ( x: 9; y: 8 ) ), + ( ( x: -1; y: 3 ), ( x: 1; y: 10 ), ( x: 0; y: 9 ) ), + ( ( x: -1; y: 3 ), ( x: -1; y: 10 ), ( x: -1; y: 9 ) ), + ( ( x: -6; y: 2 ), ( x: -7; y: 8 ), ( x: -6; y: 7 ) ), + ( ( x: -5; y: 0 ), ( x: -9; y: 5 ), ( x: -8; y: 5 ) ), + ( ( x: -1; y: -2 ), ( x: -3; y: 0 ), ( x: -2; y: 1 ) ) + ); + + +procedure DrawBackground; +begin + SetBuffer; + CheckLOS; + DrawBuffer; +end; + + +procedure DrawCharacter( x, y, d, fd, face, body, gunPic, facial, phase, gunPos : Integer ); +begin + case d of + cDirectionLeft, + cDirectionUpLeft: + begin + fTPut_Clip( x + cGunOfs[ d, gunPos].x, y + cGunOfs[ d, gunPos].y, gPics[ cGunPics[ gunPic, d, gunPos]]^, False); + fTPut_Clip( x + cBodyOfs[ d].x, y + cBodyOfs[ d].y, gPics[ cBodyPics[ body, d, phase]]^, False); + fTPut_Clip( x + cHeadOfs[ fd].x, y + cHeadOfs[ fd].y, gPics[ cFacePics[ face, fd, facial]]^, False); + end; + + cDirectionUp, + cDirectionUpRight: + begin + fTPut_Clip( x + cGunOfs[ d, gunPos].x, y + cGunOfs[ d, gunPos].y, gPics[ cGunPics[ gunPic, d, gunPos]]^, False); + fTPut_Clip( x + cHeadOfs[ fd].x, y + cHeadOfs[ fd].y, gPics[ cFacePics[ face, fd, facial]]^, False); + fTPut_Clip( x + cBodyOfs[ d].x, y + cBodyOfs[ d].y, gPics[ cBodyPics[ body, d, phase]]^, False); + end; + + else + begin + fTPut_Clip( x + cBodyOfs[ d].x, y + cBodyOfs[ d].y, gPics[ cBodyPics[ body, d, phase]]^, False); + fTPut_Clip( x + cHeadOfs[ fd].x, y + cHeadOfs[ fd].y, gPics[ cFacePics[ face, fd, facial]]^, False); + fTPut_Clip( x + cGunOfs[ d, gunPos].x, y + cGunOfs[ d, gunPos].y, gPics[ cGunPics[ gunPic, d, gunPos]]^, False); + end; + end; +end; + + +procedure DrawDeadCharacter( x, y, dc : Integer ); +begin + fTPut_Clip( x - 8, y - 3, gPics[ cDeathPics[ dc]]^, False); +end; + + +function Distance( c1, c2 : PCharacter ) : LongInt; +begin + if Abs( c1^.x - c2^.x) > Abs( c1^.y - c2^.y) then + Distance := Abs( c1^.x - c2^.x) + else + Distance := Abs( c1^.y - c2^.y); +end; + + +procedure DrawCharacters; +var xScreen, yScreen : Integer; + + procedure DrawIt( c : PCharacter ); + begin + with c^ do + begin + visible := True; + if dead and (deathCount > 0) then + DrawDeadCharacter( xScreen, yScreen, deathCount) + else begin + sleeping := False; + if (invincibility <= 0) or Odd( gFrameCounter) then + DrawCharacter( xScreen, yScreen, + direction, faceDir, face, body, gunPic, + facial, phase, gunPos); + end; + end; + end; + +var c : PCharacter; + d : Integer; +begin + gAlert1 := False; + gAlert2 := False; + c := gCharacters; + while c <> nil do + begin + with c^ do + begin + visible := False; + if InBuffer1( x, y, 12, cCharacterWidth, cCharacterHeight, xScreen, yScreen) then + DrawIt( c); + if InBuffer2( x, y, 12, cCharacterWidth, cCharacterHeight, xScreen, yScreen) then + DrawIt( c); + if id = cIdEvil then + begin + distanceToPlayer := cLargeDistance; + if gHero1 <> nil then + begin + d := Distance( gHero1, c); + if d < cProximityDistance then + gAlert1 := True; + distanceToPlayer := d; + end; + if gHero2 <> nil then + begin + d := Distance( gHero2, c); + if d < cProximityDistance then + gAlert2 := True; + if d < distanceToPlayer then + distanceToPlayer := d; + end; + end; + end; + c := c^.next; + end; +end; + + + +procedure DrawBullets; +var b : PBullet; + xScreen, yScreen : Integer; +begin + b := gBullets; + while b <> nil do + begin + with b^ do + if range <> 0 then + begin + if InBuffer1( x, y, 0, 0, 0, xScreen, yScreen) then + fTPut_Clip( xScreen, yScreen, gPics[ cShotPics[ cGunStyles[ kind].bulletStyle, pic]]^, True); + if InBuffer2( x, y, 0, 0, 0, xScreen, yScreen) then + fTPut_Clip( xScreen, yScreen, gPics[ cShotPics[ cGunStyles[ kind].bulletStyle, pic]]^, True); + end; + b := b^.next; + end; +end; + + +procedure DrawExplosions; +var i, b, pic, xScreen, yScreen : Integer; + f : PFireBall; +begin + f := gFireBalls; + while f <> nil do + begin + with f^ do + begin + pic := stage; + if (pic >= 0) and (pic <= cMaxFireBalls) then + begin + if InBuffer1( x, y, 0, 15, 15, xScreen, yScreen) then + fTPut_Clip( xScreen, yScreen, gPics[ cExplosionPics[ pic]]^, True); + if InBuffer2( x, y, 0, 15, 15, xScreen, yScreen) then + fTPut_Clip( xScreen, yScreen, gPics[ cExplosionPics[ pic]]^, True); + end; + end; + f := f^.next; + end; +end; + + +procedure DrawStatus; + + procedure ShowStatus( x : Integer; c : PCharacter; var data : PlayerData ); + var i : Integer; + color : Byte; + begin + if c <> nil then + begin + {DrawLetter( x, 10, cWhite, cBlack, St( data.mission.targets));} + if c^.armor < 10 then + color := cRed + else if c^.armor < 20 then + color := cOrange + else + color := cYellow; + Bar( x, 21, x + c^.armor*2, 25, color); + Rectangle( x, 20, x + c^.armor*2, 26, cBlack); + for i := 1 to c^.lives do + fTPut( x + i*8, 30, gPics[ cFacePics[ c^.face, 4, 0]]^, False); + if c^.gun.kind > 0 then + begin + fTPut( x, 40, gPics[ cGunStyles[ c^.gun.kind].pic]^, False); + DrawLetter( x + 20, 55, cWhite, cBlack, St( c^.gun.ammo)); + end; + end; + end; + +var objects, kills : Integer; +begin + ShowStatus( 10, gHero1, gPlayerData[0]); + ShowStatus( 160, gHero2, gPlayerData[1]); + + objects := gObjectsToCollect; + kills := gMinimumKills; + if gPlayerData[0].playing then + begin + Dec( objects, gPlayerData[0].mission.objects); + Dec( kills, gPlayerData[0].mission.kills); + end; + if gPlayerData[1].playing then + begin + Dec( objects, gPlayerData[1].mission.objects); + Dec( kills, gPlayerData[1].mission.kills); + end; + if gAlert1 then + DrawLetter( 100, 40, cBrightRed, cBlack, 'ALERT'); + if gAlert2 then + DrawLetter( 250, 40, cBrightRed, cBlack, 'ALERT'); + + if objects > 0 then + DrawLetter( 150, 10, cYellow, cBlack, St( objects)); + if kills > 0 then + DrawLetter( 150, 20, cBrightRed, cBlack, St( kills)); + if gTargetsLeft > 0 then + DrawLetter( 150, 30, cOrange, cBlack, St( gTargetsLeft)); + + if (objects <= 0) and (kills <= 0) and (gTargetsLeft <= 0) and + (gFrameCounter and 32 = 0) then + DrawLetter( 120, 30, 14, cBlack, 'MISSION COMPLETED'); + + DrawLetter( 160, 10, cWhite, cBlack, St( gMissionTime div (60*60)) + ':' + + Lz( (gMissionTime div 60) mod 60, 2)); + DrawLetter( 10, 190, cYellow, cBlack, St( gCyclesPerFrame)); +end; + + +procedure DrawRadar; +var i : Integer; +begin +{ + Bar( 146, 170, 162, 186, cBlack); + Rectangle( 146, 170, 162, 186, cWhite); + with gCharacters[0] do + if active then + PSet( 147 + ((x div 32) - cxMin) div 4, 171 + ((y div 24) - cyMin) div 4, cWhite); + with gCharacters[1] do + if active then + PSet( 147 + ((x div 32) - cxMin) div 4, 171 + ((y div 24) - cyMin) div 4, cYellow); + + for i := cBadguysStart to cMaxCharacters do + with gCharacters[i] do + if active then + PSet( 147 + ((x div 32) - cxMin) div 4, 171 + ((y div 24) - cyMin) div 4, cDarkRed); +} +end; + + +function MapCommand : Boolean; +begin + MapCommand := KeyDown( gMapKey) or + ((gHero1 <> nil) and (gHero2 <> nil) and StickButton3Down( hero1Keys.stick, hero2Keys.stick)) + or + ((gHero1 <> nil) and (gHero2 = nil) and StickButton3Down( hero1Keys.stick, 0)) + or + ((gHero2 <> nil) and (gHero1 = nil) and StickButton3Down( 0, hero2Keys.stick)); +end; + +procedure ShowMap; +var p : RGBList; + filter : RGBType; + x, y : Integer; + c : PCharacter; + + procedure SetRGB( var c : RGBType; r, g, b : Byte ); + begin + c.red := r; + c.green := g; + c.blue := b; + end; + + procedure Plot( c1, c2, c3, c4 : Integer ); + begin + PSet( 80 + x*2, 20 + y*2, c1); + PSet( 81 + x*2, 20 + y*2, c2); + PSet( 80 + x*2, 21 + y*2, c3); + PSet( 81 + x*2, 21 + y*2, c4); + end; + + procedure Plot2( c1, c2 : Integer ); + begin + PSet( 80 + x*2, 20 + y*2, c1); + PSet( 80 + x*2, 21 + y*2, c2); + end; + +begin + fGetColors( p); + SetRGB( filter, 48, 0, 48); + ColorsChange( p, filter); + SetRGB( p[254], 63, 0, 0); + SetRGB( p[253], 0, 63, 63); + p[252] := gPalette[ cWallColorFirst]; + p[251] := gPalette[ cWallColorFirst + 4]; + {SetRGB( p[252], 0, 48, 48); + SetRGB( p[251], 0, 24, 24);} + p[250] := gPalette[ cBkgColorFirst]; + p[249] := gPalette[ cBkgColorFirst + 2]; + {SetRGB( p[250], 0, 0, 48); + SetRGB( p[249], 0, 0, 24);} + SetRGB( p[248], 63, 63, 63); + SetRGB( p[247], 63, 63, 0); + fSetColors( p); + + for y := cyMin to cyMax do + for x := cxMin to cxMax do + begin + case gAutoMap[ x, y] of + + cBkgFloor, + cBkgFloorSkull, + cBkgFloorBlood, + cBkgFloorCrater: + Plot( 250, 250, 250, 250); + + cBkgBelowWall: + Plot( 249, 249, 250, 250); + + cBkgRightOfWall: + Plot( 250, 250, 249, 250); + + cBkgBelowAndRightOfWall: + Plot( 249, 250, 250, 250); + + cBkgUpLeft and cBkgFlagMask, + cBkgUpT and cBkgFlagMask, + cBkgCross and cBkgFlagMask, + cBkgLeftT and cBkgFlagMask: + Plot( 252, 252, 252, 251); + + cBkgUpEnd and cBkgFlagMask, + cBkgUpRight and cBkgFlagMask, + cBkgVert and cBkgFlagMask, + cBkgRightT and cBkgFlagMask: + Plot( 252, 249, 252, 249); + + cBkgRightEnd and cBkgFlagMask, + cBkgLeftEnd and cBkgFlagMask, + cBkgDownT and cBkgFlagMask, + cBkgDownLeft and cBkgFlagMask, + cBkgHorz and cBkgFlagMask, + cBkgHorz1 and cBkgFlagMask, + cBkgHorz2 and cBkgFlagMask, + cBkgHorz3 and cBkgFlagMask, + cBkgHorz4 and cBkgFlagMask, + cBkgHorz5 and cBkgFlagMask: + Plot( 252, 252, 251, 251); + + cBkgDownRight and cBkgFlagMask, + cBkgDownEnd and cBkgFlagMask: + Plot( 252, 249, 251, 249); + + cBkgHorzDoor and cBkgFlagMask, + cBkgShadow:; + + cBkgShadowRightT, + cBkgShadowVert, + cBkgShadowUpRight, + cBkgShadowUpEnd: + Plot2( 252, 252); + + cBkgShadowDownRight, + cBkgShadowDownEnd: + Plot2( 252, 251); + + cBkgFloorItem1, + cBkgFloorItem2, + cBkgFloorItem3, + cBkgFloorItem4, + cBkgFloorItem5, + cBkgFloorItem6, + cBkgFloorItem7, + cBkgFloorArmorAddOn, + cBkgFloorAxe: + Plot( 253, 253, 249, 249); + + cBkgFloorDocs, + cBkgFloorFolder, + cBkgFloorDisk, + cBkgFloorCircuit, + cBkgFloorTeddy: + Plot( 254, 254, 249, 249); + + cBkgChaosBox and cBkgFlagMask: + Plot( 254, 254, 254, 254); + + cBkgExit: + Plot( 253, 253, 253, 253); + + else + Plot( 249, 249, 249, 249); + + end; + end; + + if gHero1 <> nil then + with gHero1^ do + begin + PSet( 80 + x div 16, 20 + y div 12, 248); + PSet( 80 + x div 16, 21 + y div 12, 249); + Rectangle( 69 + x div 16, 11 + y div 12, + 91 + x div 16, 30 + y div 12, 248); + end; + if gHero2 <> nil then + with gHero2^ do + begin + PSet( 80 + x div 16, 20 + y div 12, 247); + PSet( 80 + x div 16, 21 + y div 12, 249); + Rectangle( 69 + x div 16, 11 + y div 12, + 91 + x div 16, 30 + y div 12, 247); + end; + + PCopyDW( pages[2]^, pages[1]^); + while MapCommand do + PollSticks; + gVBlankCounter := 0; +end; + + +procedure Frame; +begin + SetPageActive(2); + DrawBackground; + DrawCharacters; + DrawBullets; + DrawExplosions; + SetClipRange( 0, 0, 319, 199); + DrawStatus; + DrawRadar; + + if gVBlankCounter < 4 then + VSinc; + Dec( gVBlankCounter, 4); + if gVBlankCounter < 0 then + gVBlankCounter := 0; + + if MapCommand then + begin + ShowMap; + PCopyDW( pages[2]^, pages[1]^); + fSetColors( gPalette); + end + else + PCopyDW( pages[2]^, pages[1]^); + + Inc( gFrameCounter); +end; + + +procedure FillScreen( pic : Integer ); +var x, y : Integer; + offset : Word; + picPtr : Pointer; + w, h : Integer; +begin + offset := 0; + picPtr := gPics[ pic]; + ImageDims( picPtr^, w, h); + if (w <> 32) or (h <> 20) then + Exit; + + for y := 0 to 9 do + begin + for x := 0 to 9 do + begin + fPutDW( offset, picPtr^); + {fPut( x*32, y*20, picPtr^, False);} + Inc( offset, 32); + end; + Inc( offset, 320*19); + end; +end; + + +end. diff --git a/sounds.pas b/sounds.pas new file mode 100644 index 0000000..785f6a3 --- /dev/null +++ b/sounds.pas @@ -0,0 +1,312 @@ +unit Sounds; + + +interface + + +const + + cBangSound = 1; + cScreamSound = 2; + cBigGunSound = 3; + cLaunchSound = 4; + cPickupSound = 5; + cSmallGunSound = 6; + cMinigunSound = 7; + cSwitchSound = 8; + cFlamerSound = 9; + cLaserSound = 10; + cSmallBangSound = 11; + cChainsawSound = 12; + cSoundMax = 12; + + +procedure InitializeSound( irq, dma : Integer; quality, use486, tsOnly : Boolean ); +procedure CloseDownSound; +procedure DoSound( sound : Integer ); +procedure DoSoundEffects; +procedure PlaySound( sound : Integer ); +procedure PlayMusic( song : String ); +procedure StopMusic; +function GetNextLevelSong : String; + + +type + + PLevelSong = ^LevelSong; + LevelSong = + record + song : String[8]; + next : PLevelSong; + end; + + +const + + gMusic : Boolean = False; + gSoundFX : Boolean = False; + gMenuSong : String[8] = 'cebit90'; + gCreditsSong : String[8] = 'cebit90'; + gFailureSong : String[8] = 'cebit90'; + gSuccessSong : String[8] = 'cebit90'; + gHallOfFameSong : String[8] = 'cebit90'; + gLevelSongs : PLevelSong = nil; + gSoundFiles : array [1..cSoundMax] of String[8] = + ( 'BANG', 'KILL', 'POWERGUN', 'LAUNCH', 'PICKUP', + 'BLASTER', 'MINIGUN', 'SWITCH', 'FLAME', 'LASER', 'POOF', 'CHAINSAW' ); + + +implementation + +{ DSMI.Inc contains a USES clause with all DSMI units } +{$I DSMI.Inc}; + +const + + uPriority : Integer = 0; + uLastSamplePos : LongInt = 0; + +const + + uSoundPriorities : array [1..cSoundMax] of Integer = + ( 10, 9, 5, 6, 8, 4, 7, 6, 6, 5, 8, 3 ); + {uSongFiles : array [1..cSongMax] of String[8] = + ( 'CEBIT90', + 'COMPATIL', 'KOL_DOL', 'FUTURE', 'TECH-ROC', '003', 'DRAGLAIR', + 'SYMF2010', 'GNUTTEN', 'COMA' );} + + cSampleFreq = 11000; + cSampleVolume = 64; + + uSoundOn : Boolean = False; + uModule : PModule = nil; + uSoundChannel : Integer = 0; + + +var + + uSoundFlags : array [1..cSoundMax] of Boolean; + uSoundData : array [1..cSoundMax] of TSampleInfo; + isGus : Boolean; + + +procedure LoadRawSound( name : String; var sample : TSampleInfo ); +var f : File; +begin + sample.sample := nil; + Assign( f, name + '.RAW'); + Reset( f, 1); + if IOResult <> 0 then + Exit; + + sample.sample := Malloc( FileSize( f)); + if sample.sample = nil then + begin + Close( f); + Exit; + end; + BlockRead( f, sample.sample^, FileSize( f)); + with sample do + begin + length := FileSize( f); + loopstart := 0; + loopend := 0; { No looping } + mode := 0; + sampleID := 0; + end; + cdiDownLoadSample( 0, sample.sample, sample.sample, sample.length); + Close( f); +end; + + +procedure InitializeSound( irq, dma : Integer; + quality, use486, tsOnly : Boolean ); +var i : Integer; + sc : TSoundCard; + options : Integer; +begin + options := 0; + if quality then + options := options or MCP_QUALITY; + if use486 then + options := options or MCP_486; + {if initDSMI( 22000, 2048, 0, @sc) <> 0 then} + if tsOnly or (initDSMI( 22000, 2048, options, irq, dma, @sc) <> 0) then + begin + tsInit; + atExit( @tsClose); + Exit; + end; + if sc.ID <> ID_GUS then + mcpStartVoice + else + gusStartVoice; + cdiSetupChannels( 0, 2, nil); + uSoundOn := True; + {FillChar( uSoundFlags, SizeOf( uSoundFlags), 0); + FillChar( uSoundData, SizeOf( uSoundData), 0); + + WriteLn( 'Loading SB driver...'); + if LoadDriver( '\SBPRO\DRV\CT-VOICE.DRV') <> LoadDrvSuccess then + Exit; + WriteLn( 'Setting interrupt'); + SetInterrupt( 5); + WriteLn( 'Setting base IO address'); + SetBaseIOAddress( $220); + WriteLn( 'Initializing driver...'); + if InitSB <> SBInitSuccess then + Exit; + uSB := True; + WriteLn( 'Initializing status word'); + InitStatusWord; + WriteLn( 'Turning speaker on'); + TurnSpeakerOn;} + WriteLn( 'Loading sound files...'); + for i := 1 to cSoundMax do + LoadRawSound( gSoundFiles[ i], uSoundData[ i]); + gMusic := True; + gSoundFX := True; +end; + + +procedure CloseDownSound; +var i : Integer; +begin + if not uSoundOn then + Exit; + + StopMusic; + cdiStopNote( uSoundChannel); + cdiStopNote( uSoundChannel+1); + for i := 1 to cSoundMax do + with uSoundData[ i] do + if sample <> nil then + Free( sample); +end; + + +procedure DoSound( sound : Integer ); +begin + uSoundFlags[ sound] := True; +end; + + +procedure PlaySound( sound : Integer ); +begin + if uPriority > 0 then + begin + cdiStopNote( uSoundChannel); + cdiStopNote( uSoundChannel+1); + uPriority := 0; + end; + DoSound( sound); + DoSoundEffects; +end; + + +procedure DoSoundEffects; + + procedure DoSound; + var i : Integer; + begin + for i := 1 to cSoundMax do + if uSoundFlags[ i] and + (uSoundData[ i].sample <> nil) and + (uSoundPriorities[i] > uPriority) then + begin + if uPriority > 0 then + begin + cdiStopNote( uSoundChannel); + cdiStopNote( uSoundChannel+1); + end; + uPriority := uSoundPriorities[i]; + cdiSetInstrument( uSoundChannel, @uSoundData[i]); + cdiSetInstrument( uSoundChannel+1, @uSoundData[i]); + cdiPlayNote( uSoundChannel, cSampleFreq, cSampleVolume); + cdiPlayNote( uSoundChannel+1, cSampleFreq, cSampleVolume); + Exit; + end; + end; + +var pos : LongInt; +begin + if not (uSoundOn and gSoundFX) then + Exit; + + if uPriority > 0 then + begin + {if cdiGetChannelStatus( uSoundChannel) and ch_Playing = 0 then + uPriority := 0;} + pos := cdiGetPosition( uSoundChannel); + if pos = uLastSamplePos then + uPriority := 0 + else + uLastSamplePos := pos; + end; + + DoSound; + FillChar( uSoundFlags, SizeOf( uSoundFlags), 0); +end; + + +procedure StopMusic; +var i : Integer; +begin + if uModule <> nil then + begin + ampStopModule; + ampFreeModule( uModule); + uModule := nil; + + { This is a patch for a bug in ampStopModule in the previous release. + That version unloads ALL samples thus forcing me to + reload them again. + This is supposedly fixed in the current release. + BUT...the current release slows my game WAY down on + some machines only...weeiird... } + + for i := 1 to cSoundMax do + with uSoundData[ i] do + if sample <> nil then + cdiDownLoadSample( 0, sample, sample, length); + end; +end; + + +function GetNextLevelSong : String; +var h : ^PLevelSong; + s : PLevelSong; +begin + if gLevelSongs = nil then + begin + GetNextLevelSong := ''; + Exit; + end; + h := @gLevelSongs; + while h^^.next <> nil do + h := @h^^.next; + s := h^; + h^ := nil; + s^.next := gLevelSongs; + gLevelSongs := s; + GetNextLevelSong := s^.song; +end; + + +procedure PlayMusic( song : String ); +begin + if not gMusic then + Exit; + + StopMusic; + uModule := ampLoadAMF( song + '.AMF', 0); + if uModule = nil then + Exit; + cdiSetupChannels( 0, uModule^.channelCount + 2, nil); + uSoundChannel := uModule^.channelCount; + cdiSetMasterVolume( 0, 64); + ampPlayModule( uModule, PM_LOOP); +end; + + +end. diff --git a/stick.pas b/stick.pas new file mode 100644 index 0000000..98d0ad5 --- /dev/null +++ b/stick.pas @@ -0,0 +1,116 @@ +unit Stick; + + +interface + + +function StickDown( s : Byte ) : Boolean; +function StickUp( s : Byte ) : Boolean; +function StickLeft( s : Byte ) : Boolean; +function StickRight( s : Byte ) : Boolean; +function StickAnyButton( s : Byte ) : Boolean; +function StickButton1( s : Byte ) : Boolean; +function StickButton2( s : Byte ) : Boolean; +function StickAction( s : Byte ) : Boolean; +function StickButtonPressed : Boolean; +function StickButton3Down( stick1, stick2 : Integer ) : Boolean; + + +const + + gStickLeft : array [1..2] of Integer = ( 10, 10); + gStickRight : array [1..2] of Integer = ( 25, 25); + gStickUp : array [1..2] of Integer = ( 10, 10); + gStickDown : array [1..2] of Integer = ( 25, 25); + + +implementation + + uses Joystick; + + +function StickDown( s : Byte ) : Boolean; +begin + if s = 0 then + StickDown := StickDown( 1) or StickDown( 2) + else + StickDown := gSticks[s].present and (gSticks[ s].y > gStickDown[ s]); +end; + +function StickUp( s : Byte ) : Boolean; +begin + if s = 0 then + StickUp := StickUp( 1) or StickUp( 2) + else + StickUp := gSticks[s].present and (gSticks[s].y < gStickUp[s]);; +end; + +function StickLeft( s : Byte ) : Boolean; +begin + if s = 0 then + StickLeft := StickLeft( 1) or StickLeft( 2) + else + StickLeft := gSticks[s].present and (gSticks[ s].x < gStickLeft[ s]); +end; + +function StickRight( s : Byte ) : Boolean; +begin + if s = 0 then + StickRight := StickRight( 1) or StickRight( 2) + else + StickRight := gSticks[s].present and (gSticks[ s].x > gStickRight[ s]); +end; + +function StickAnyButton( s : Byte ) : Boolean; +begin + if s = 0 then + StickAnyButton := StickAnyButton(1) or StickAnyButton(2) + else + StickAnyButton := gSticks[s].present and + (gSticks[s].button1 or gSticks[s].button2); +end; + +function StickButton1( s : Byte ) : Boolean; +begin + if s = 0 then + StickButton1 := StickButton1(1) or StickButton1(2) + else + StickButton1 := gSticks[s].present and gSticks[s].button1; +end; + +function StickButton2( s : Byte ) : Boolean; +begin + if s = 0 then + StickButton2 := StickButton2(1) or StickButton2(2) + else + StickButton2 := gSticks[s].present and gSticks[s].button2; +end; + +function StickAction( s : Byte ) : Boolean; +begin + StickAction := StickUp( s) or StickDown( s) or + StickLeft( s) or StickRight( s) or + StickAnyButton( s); +end; + +function StickButtonPressed : Boolean; +begin + PollSticks; + StickButtonPressed := StickAnyButton( 0); +end; + + +function StickButton3Down( stick1, stick2 : Integer ) : Boolean; +begin + StickButton3Down := False; + if ((stick1 <> 0) and (stick2 <> 0)) or + ((stick1 = 0) and (stick2 = 0)) then + Exit; + if stick1 <> 0 then + StickButton3Down := gSticks[stick1].button3 + else + StickButton3Down := gSticks[stick2].button3; +end; + + +end.