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.