'===========================================================================
' Subject: SPACE WAR!!!                       Date: 04-01-97 (21:00)       
'  Author: Gary N. Wilkerson, Jr.             Code: QB, QBasic, PDS        
'  Origin: comp.lang.basic.misc             Packet: GAMES.ABC
'===========================================================================
'                         S P A C E    W A R ! ! !
'                   1987..1993 by Gary N. Wilkerson Jr.
'
' If speed of game is too slow, change the SPEEDCONST = 500 line.
' The higher the number, the faster the game will go.
'
' To run this game press Shift+F5.
' To exit Qbasic, press Alt, F, X.
'
' Sparklepause, CalcDelay, Center, and Rest procedures came from
' QBasic Nibbles and Gorillas program which may be found in the DOS
' directory of the hard drive.


' Set default data type to integer for faster game play
DEFINT A-Z

' The SINGLE types allow for various angles of motion
TYPE AsteroidType
    Vert AS SINGLE
    Horiz AS SINGLE
    DirVert AS SINGLE
    DirHoriz AS SINGLE
    Smash AS INTEGER
    TorF AS INTEGER
    Shot AS INTEGER
END TYPE

' Type for the player array.
' LONG type allows for large scores, while STRING allows only 10 characters.
TYPE PlayrType
    PlayrName AS STRING * 10
    Score AS LONG
    HorizPos AS INTEGER
    VertPos AS INTEGER
    HorizDir AS INTEGER
    VertDir AS INTEGER
    ShotHoriz AS INTEGER
    ShotVert AS INTEGER
    Alive AS INTEGER
    Shot AS INTEGER
    Joystick AS INTEGER
END TYPE

TYPE StickType
    Min AS INTEGER
    Max AS INTEGER
END TYPE

DECLARE SUB Initialize ()
DECLARE SUB GameSetUp ()
DECLARE SUB PlaySpWar ()
DECLARE SUB FindShip ()
DECLARE SUB TallyScore (Playr)
DECLARE SUB FireBullet (Num)
DECLARE SUB EndScene ()
DECLARE SUB ObjectScreen ()
DECLARE SUB ControlScreen ()
DECLARE SUB OnOff (text$, cond)
DECLARE SUB FuncKeyOn ()
DECLARE SUB FuncKeyOff ()
DECLARE SUB FuncKeyDisplay ()
DECLARE SUB InitPlayArea ()
DECLARE SUB MoveBullets ()
DECLARE SUB MoveAstrd ()
DECLARE SUB Center (row, text$)
DECLARE SUB KeyPause ()
DECLARE SUB SparklePause ()
DECLARE SUB GetDirections ()
DECLARE SUB MoveShips ()
DECLARE SUB DsplyScore (p1&, p2&, cond)
DECLARE SUB Explode (p1, p2)
DECLARE SUB TimeDsply (cond)
DECLARE SUB Rest (t#)
DECLARE FUNCTION Yes ()
DECLARE FUNCTION CalcDelay! ()
DECLARE FUNCTION ShipShot ()

' Constants
CONST SPEEDCONST = 500
CONST TRUE = -1
CONST FALSE = NOT TRUE
CONST leftbound = 0
CONST rightbound = 599
CONST upbound = 29
CONST downbound = 169

' Select the graphics screen
' Requires CGA, EGA, VGA, or MCGA Adapters
ON ERROR GOTO GraphicsError
SCREEN 2: ON ERROR GOTO 0

' Global variables and graphics arrays
size = 4 + INT(((PMAP(40, 0) - PMAP(0, 0) + 1) * 2 + 7) / 8) * (PMAP(30, 1) - PMAP(0, 1) + 1)
DIM SHARED OnePlayr%(size), TwoPlayr%(size)
DIM SHARED Player(1 TO 2) AS PlayrType
DIM SHARED Asteroid%(size), Astrd AS AsteroidType
DIM SHARED Joystick(3) AS StickType
DIM SHARED Temp$(5)
DIM SHARED Lives AS INTEGER
DIM SHARED PlayrNum AS INTEGER
DIM SHARED SoundOn AS INTEGER
DIM SHARED MachSpeed AS INTEGER
DIM SHARED Time AS INTEGER
DIM SHARED JoyFlag AS INTEGER

' Define what the ships will look like
ship$ = "s2h10r15f10r20f10g10l20g10l15e10u20d4nl8d4nl12d4nl16d4nl12d4l8s4"

' Draw player 2's ship and store in array
DRAW "bm14,10x" + VARPTR$(ship$): GET (0, 0)-(40, 30), TwoPlayr%

' Draw player 1's ship and store in array
CLS
DRAW "bm26,20a2x" + VARPTR$(ship$): GET (0, 0)-(40, 30), OnePlayr%

' Define asteroid, draw it, and store in array
Asteroid$ = "BM5,10r5h5r5f5e5f5e5r5g5r5d5g5r5d5l15h5g5l5u5e5h5"
CLS : DRAW Asteroid$: GET (0, 0)-(40, 30), Asteroid%

Initialize
DO
    ObjectScreen
    ControlScreen
    GameSetUp
    InitPlayArea
    FuncKeyOn

    PlaySpWar
    EndScene
    COLOR 15, 0: Center 22, "Play again"
LOOP WHILE Yes
SYSTEM

GraphicsError:
     ' This system does not have a color graphics adapter
     CLS : Center 11, "Sorry you must have CGA, EGA, VGA, or MCGA Adapters"
     Center 12, "To play SPACE WAR"
     PRINT : SYSTEM

StickError:
    ' This system does not support joysticks
    JoyFlag = FALSE
    RESUME NEXT

HelpScreen:
    FuncKeyOff
    DO
        SCREEN 0: CLS
        COLOR 14, 0
        Center 8, "Space War Help Screen"
        COLOR 10, 0: Center 11, "Press [ESC] to see the player controls"
        Center 12, "Press [ENTER] to see the object screen"
        Center 17, "Press any other key to continue"
       
        SELECT CASE INPUT$(1)
            CASE CHR$(27): ControlScreen
            CASE CHR$(13): ObjectScreen
            CASE ELSE: EXIT DO
        END SELECT
    LOOP
    InitPlayArea
    FuncKeyOn
    RETURN

DosHide:
    FuncKeyOff
    SCREEN 0: COLOR 7, 0: CLS
    SHELL
    InitPlayArea
    FuncKeyOn
    RETURN

SoundToggle:
    SoundOn = NOT SoundOn
    FuncKeyDisplay
    RETURN

Pause:
    FuncKeyOff
    Center 11, "Game Paused"
    Center 12, "Press any key to continue"
    SLEEP
    Center 11, SPACE$(11)
    Center 12, SPACE$(25)
    FuncKeyOn
    RETURN

Quit:
    FuncKeyOff
    Center 11, "You pressed [QUIT]!"
    Center 12, "Are you sure"
    IF Yes THEN SYSTEM
    Center 11, SPACE$(19)
    Center 12, SPACE$(22)
    FuncKeyOn
    RETURN

'CalcDelay:
'  Checks speed of the machine.
FUNCTION CalcDelay!
  s! = TIMER
  DO
    i! = i! + 1
  LOOP UNTIL TIMER - s! >= .5
  CalcDelay! = i!
END FUNCTION

' Center, centers (text$) on given (row.)
SUB Center (row, text$)
     LOCATE row, 40 - LEN(text$) / 2
     PRINT text$;
END SUB

' ControlScreen displays the games controls
SUB ControlScreen
     CLS
     COLOR 15
     Center 7, " Game Controls "
     Center 9, "  General             Player 1               Player 2    "
     Center 10, "                        (Up)                   (Up)      "
     Center 11, "                         " + CHR$(24) + "                      W       "
     Center 12, "                     (Left) " + CHR$(27) + "   " + CHR$(26) + " (Right)   (Left) A   D (Right)  "
     Center 13, "                         " + CHR$(25) + "                      S       "
     Center 14, "                       (Down)                 (Down)     "
     Center 16, "                    (Fire)  [ESC]         (Fire)  [SPACE]"
     Center 24, "Press any key to continue"

     SparklePause
END SUB

' DsplyScore displays the players' scores
SUB DsplyScore (p1&, p2&, cond)
     ' Tally the score and keep it within LONG INT's limits
     Player(1).Score = (Player(1).Score + p1&) AND &H7FFFFFFF
     Player(2).Score = (Player(2).Score + p2&) AND &H7FFFFFFF
     Lives = Lives + cond

     ' Score 999999 is the limit for PRINT USING
     FOR t = 1 TO 2
        IF Player(t).Score > 999999 THEN Player(t).Score = 999999
     NEXT t

     IF PlayrNum = 1 THEN
        ' One player game score format
        LOCATE 1, 1: PRINT USING "Score ###,###"; Player(1).Score
        LOCATE 1, 64: PRINT USING "Lives #"; Lives
     ELSE
        ' Two player game score format
        LOCATE 1, 1: PRINT Player(2).PlayrName;
        PRINT USING " - ###"; Player(2).Score
        LOCATE 1, 64: PRINT Player(1).PlayrName;
        PRINT USING " - ###"; Player(1).Score
     END IF
END SUB

' End Scene ends the game and determines the winner
SUB EndScene
    FuncKeyOff
    ' Draws the concecrated circles of the end scene
    Center 13, "The End"
    PLAY "T255L64O1"
    FOR y = 15 TO 0 STEP -15
        FOR t = 50 TO 500 STEP 20
            CIRCLE (320, 100), t, y, , , .25
            IF SoundOn THEN PLAY "a"
        NEXT t
    NEXT y
    PLAY "T160L4"
    SLEEP 2

    SCREEN 0: CLS : COLOR 15, 0
    IF PlayrNum = 2 THEN
        FOR t = 1 TO 2
            COLOR 4, 0
            Center ((t - 1) * 5 + 5), "Player 1 -- " + Player(t).PlayrName
            Center ((t - 1) * 5 + 6), "Score -" + STR$(Player(t).Score)
        NEXT t
        COLOR 14, 0
        Center 13, "The Winner is:"
        COLOR 15, 0
        IF Player(1).Score < Player(2).Score THEN
            Center 15, Player(2).PlayrName
        ELSE
            Center 15, Player(1).PlayrName
        END IF
    ELSE
        PLAY "T255L64O4"
        Center 8, "Lives X Hits X 10 = Bonus"
        COLOR 11, 0
        Center 10, STR$(Lives) + " X" + STR$(NumbrHits) + " =" + STR$(Lives * NumbrHits) + " X 10"
        FOR t = 1 TO Lives * NumbrHits
            DsplyScore 10, 0, 0
            IF SoundOn THEN PLAY "a"
        NEXT t
        COLOR 14, 0
        Center 13, "Total score" + STR$(Player(1).Score)
        Center 14, "The End"
    END IF
END SUB

' Explode is self explanatory
SUB Explode (p1, p2)
        ' Sound effect
        IF SoundOn THEN PLAY "MBO0L16efgefedc"
        ' Draw explosion
        FOR i# = 2 TO 1 STEP -1
                FOR y = 1 TO 50 STEP i#
                        CIRCLE (p1 + 20, p2 + 15), y, (i# - 1) * 4
                        FOR z = 1 TO 500: NEXT z
                NEXT y
        NEXT i#
END SUB

' FindShip determines which ship to blow up.
SUB FindShip
    FOR t = 1 TO 2
        IF NOT (Player(t).Alive) THEN
            Explode Player(t).HorizPos, Player(t).VertPos
            TallyScore t
            Player(t).HorizPos = (t AND 1) * 599
            Player(t).VertPos = 100
            Player(t).Alive = TRUE
        END IF
    NEXT t
END SUB

' FireBullet is self explanatory
SUB FireBullet (Num)
    ' Position bullet
    Player(Num).ShotHoriz = Player(Num).HorizPos + ((Num - 1) * 40)
    Player(Num).ShotVert = Player(Num).VertPos + 15
    ' Make sound effect
    IF SoundOn THEN PLAY "MBO" + STR$(Num + 3) + "L64bagfedcbagfedcL4"
    ' Make bullet active
    Player(Num).Shot = TRUE
END SUB

' FuncKeyDisplay displays the active function key and their effects.
SUB FuncKeyDisplay
     LOCATE 3, 1
     PRINT "[F1] -- Help", "[F2] -- Hide",
     OnOff "[F3] -- Sound", SoundOn
     PRINT "  [F4] -- Pause    [F5] -- Quit "
END SUB

' FuncKeyOff turns all the key traps off.
SUB FuncKeyOff
     FOR Func = 1 TO 5
        KEY(t) OFF
     NEXT Func
END SUB

' FuncKeyOn turns all the key traps on.
SUB FuncKeyOn
     FOR Func = 1 TO 5
        KEY(t) ON
     NEXT Func
END SUB

SUB GameSetUp
    COLOR 15, 0: CLS : LOCATE 5, 1
    PRINT "Press [1] or [2] players"
    DO: a$ = INPUT$(1): LOOP WHILE a$ < "1" OR a$ > "2"
    PlayrNum = VAL(a$)
   
    ' Set up beginning coordinates and direction
    FOR t = 1 TO 2
        Player(t).HorizPos = (t AND 1) * 599: Player(t).VertPos = 100
        Player(t).HorizDir = (t - 1) * (PlayrNum AND 1) * 4
        Player(t).VertDir = (t - 1) * (PlayrNum AND 1) * 4
        Player(t).Score = 0: Player(t).Alive = TRUE
        Player(t).ShotHoriz = ((t XOR 1) AND 1) * 641 - 1
        Player(t).Shot = FALSE
        Player(t).Joystick = FALSE
    NEXT t

    FOR t = 1 TO PlayrNum
        PRINT : PRINT "Enter name player"; t
        LINE INPUT Player(t).PlayrName
        IF Player(t).PlayrName = SPACE$(10) THEN
                Player(t).PlayrName = "Player" + STR$(t)
        END IF
        IF JoyFlag THEN
            PRINT "Do you want to use joystick";
            IF Yes THEN
                Player(t).Joystick = TRUE
                PRINT : PRINT "Press [ESC] to exit"
                PRINT "Move joystick to upper left, and press fire: ";
                DO
                    IF INKEY$ = CHR$(27) THEN Player(t).Joystick = FALSE: EXIT DO
                    ' Get current minimum joystick coordinates
                    Joystick(t * 2 - 2).Min = STICK(t * 2 - 2) + 10
                    Joystick(t * 2 - 1).Min = STICK(t * 2 - 1) + 10
                LOOP UNTIL STRIG(t * 2 - 1) OR STRIG(t * 2 + 3)
                PRINT "OK"
                PRINT "Move joystick to lower right, and press fire: ";
                WHILE STRIG(t * 2 - 1) OR STRIG(t * 2 + 3): WEND
                DO
                    IF INKEY$ = CHR$(27) THEN Player(t).Joystick = FALSE: EXIT DO
                    ' Get current maximum joystick coordinates
                    Joystick(t * 2 - 2).Max = STICK(t * 2 - 2) - 10
                    Joystick(t * 2 - 1).Max = STICK(t * 2 - 1) - 10
                LOOP UNTIL STRIG(t * 2 - 1) OR STRIG(t * 2 + 3)
                PRINT "OK"
            END IF
        END IF
    NEXT t

    MachSpeed = CalcDelay

    Lives = 5: NumbrHits = 0: Time = 255
    Astrd.Smash = FALSE: Astrd.TorF = FALSE: Astrd.Shot = FALSE
END SUB

SUB GetDirections
    a$ = UCASE$(INKEY$)
    ' Get joystick direction, if player is using joystick
    IF Player(1).Joystick THEN
        SELECT CASE STICK(0)
            CASE IS < Joystick(0).Min: Player(1).HorizDir = -4
            CASE IS > Joystick(0).Max: Player(1).HorizDir = 4
            CASE ELSE: Player(1).HorizDir = 0
        END SELECT
        SELECT CASE STICK(1)
            CASE IS < Joystick(1).Min: Player(1).VertDir = -4
            CASE IS > Joystick(1).Max: Player(1).VertDir = 4
            CASE ELSE: Player(1).VertDir = 0
        END SELECT
        IF STRIG(0) OR STRIG(4) THEN FireBullet 1
    ELSE
        ' Get direction from keyboard, if player is using keyboard
        SELECT CASE a$
            CASE CHR$(0) + "H": Player(1).VertDir = -4: Player(1).HorizDir = 0
            CASE CHR$(0) + "K": Player(1).HorizDir = -4: Player(1).VertDir = 0
            CASE CHR$(0) + "P": Player(1).VertDir = 4: Player(1).HorizDir = 0
            CASE CHR$(0) + "M": Player(1).HorizDir = 4: Player(1).VertDir = 0
            CASE " "
                Player(1).ShotHoriz = Player(1).HorizPos
                Player(1).ShotVert = Player(1).VertPos + 15
                IF SoundOn THEN PLAY "MBO4L64bagfedcbagfedcL4"
                Player(1).Shot = TRUE
        END SELECT
    END IF
    IF PlayrNum = 2 THEN
        ' Get joystick direction, if player is using joystick
        IF Player(2).Joystick THEN
            SELECT CASE STICK(2)
                CASE IS < Joystick(2).Min: Player(2).HorizDir = -4
                CASE IS > Joystick(2).Max: Player(2).HorizDir = 4
                CASE ELSE: Player(2).HorizDir = 0
            END SELECT
            SELECT CASE STICK(3)
                CASE IS < Joystick(3).Min: Player(2).VertDir = -4
                CASE IS > Joystick(3).Max: Player(2).VertDir = 4
                CASE ELSE: Player(2).VertDir = 0
            END SELECT
            IF STRIG(2) OR STRIG(6) THEN FireBullet 2
        ELSE
            ' Get direction from keyboard, if player is using keyboard
            SELECT CASE a$
                CASE "W": Player(2).VertDir = -4: Player(2).HorizDir = 0
                CASE "A": Player(2).HorizDir = -4: Player(2).VertDir = 0
                CASE "S": Player(2).VertDir = 4: Player(2).HorizDir = 0
                CASE "D": Player(2).HorizDir = 4: Player(2).VertDir = 0
                CASE CHR$(27)
                    Player(2).ShotHoriz = Player(2).HorizPos + 40
                    Player(2).ShotVert = Player(2).VertPos + 15
                    IF SoundOn THEN PLAY "MBO5L64bagfedcbagfedcL4"
                    Player(2).Shot = TRUE
            END SELECT
        END IF
    END IF
END SUB

SUB Initialize
    SCREEN 0: COLOR 15, 0
    KEY OFF
    FOR t = 1 TO 5: KEY t, "": NEXT t
    IF UCASE$(COMMAND$) <> "-S" THEN
        SoundOn = TRUE
    ELSE
        SoundOn = FALSE
    END IF
    ON KEY(1) GOSUB HelpScreen
    ON KEY(2) GOSUB DosHide
    ON KEY(3) GOSUB SoundToggle
    ON KEY(4) GOSUB Pause
    ON KEY(5) GOSUB Quit

    ' This is a clever method to detect whether the system
    '   supports joysticks.
    JoyFlag = TRUE
    ON ERROR GOTO StickError
    a = STICK(0): ON ERROR GOTO 0
END SUB

' Reinitialize Graphics Mode 2
SUB InitPlayArea
    ON ERROR GOTO GraphicsError
    SCREEN 2: ON ERROR GOTO 0

    Center 11, "Initializing playfield"
    ' Draw 300 stars
    FOR t = 1 TO 300: PSET (RND * 640, RND * 200): NEXT t
    ' Draw line separator
    LINE (0, 28)-(640, 28)

    Center 11, SPACE$(21)
    TimeDsply 0
    DsplyScore 0, 0, 0

    FuncKeyDisplay

    PUT (Player(1).HorizPos, Player(1).VertPos), OnePlayr%, PSET    ' Put both players
    PUT (Player(2).HorizPos, Player(2).VertPos), TwoPlayr%, PSET    ' on screen

    Center 11, "Press any key when ready"
    KeyPause
    Center 11, SPACE$(24)
END SUB

' KeyPause is self explanatory
SUB KeyPause
     WHILE INKEY$ <> "": WEND      ' Clear keyboard
     WHILE INKEY$ = "": WEND       ' Wait for key
END SUB

SUB MoveAstrd
     ' Exit if Asteroid does not exist
     IF NOT (Astrd.TorF) THEN EXIT SUB

     Astrd.Horiz = Astrd.Horiz + Astrd.DirHoriz
     Astrd.Vert = Astrd.Vert + Astrd.DirVert

     ' Check to see if either ship shot asteroid
     FOR t = 1 TO 2
        IF Player(t).Shot THEN
                IF Astrd.Horiz + 40 < Player(t).ShotHoriz THEN GOTO missastrd
                IF Astrd.Horiz > Player(t).ShotHoriz THEN GOTO missastrd
                IF Astrd.Vert + 30 < Player(t).ShotVert THEN GOTO missastrd
                IF Astrd.Vert > Player(t).ShotVert THEN GOTO missastrd
                Astrd.Shot = TRUE: Astrd.TorF = FALSE: Player(t).Shot = FALSE
missastrd: END IF
     NEXT t

     ' Remove asteroid if it goes out of bounds
     IF Astrd.Horiz < leftbound OR Astrd.Horiz > rightbound THEN
        Astrd.TorF = FALSE: Astrd.Horiz = Astrd.Horiz - Astrd.DirHoriz
     END IF
     IF Astrd.Vert < upbound OR Astrd.Vert > downbound THEN
        Astrd.TorF = FALSE: Astrd.Vert = Astrd.Vert - Astrd.DirVert
     END IF

     IF Astrd.TorF THEN
        PUT (Astrd.Horiz, Astrd.Vert), Asteroid%, PSET
     ELSE
        LINE (Astrd.Horiz, Astrd.Vert)-(Astrd.Horiz + 40, Astrd.Vert + 30), 0, BF
     END IF
END SUB

SUB MoveBullets
     FOR t = 1 TO 2
        ' Move player's bullet, only when it's active.
        IF Player(t).Shot THEN
          ' Erase bullet
          LOCATE Player(t).ShotVert / 8 + 1, Player(t).ShotHoriz / 8 + 1
          PRINT " ";

          ' Move bullet
          IF t = 1 THEN
               Player(1).ShotHoriz = Player(1).ShotHoriz - 8
          ELSE
               Player(2).ShotHoriz = Player(2).ShotHoriz + 8
          END IF
        ELSE
          ' If computer's bullet inactive, have computer refire the bullet.
          IF PlayrNum = 1 AND t = 2 THEN
               Player(2).ShotHoriz = Player(2).HorizPos + 40
               Player(2).ShotVert = Player(2).VertPos + 15
               Player(2).Shot = TRUE
          END IF
        END IF

        ' If bullet goes out of bounds, make bullet inactive.
        IF t = 1 THEN
          IF Player(1).ShotHoriz < 0 THEN Player(1).Shot = FALSE
        ELSE
          IF Player(2).ShotHoriz > 639 THEN Player(2).Shot = FALSE
        END IF

        ' Plot the shot of the active bullet.
        IF Player(t).Shot THEN
          LOCATE Player(t).ShotVert / 8 + 1, Player(t).ShotHoriz / 8 + 1
          PRINT "*";
        END IF
     NEXT t
END SUB

' Moves both the ships on the screen
SUB MoveShips

     ' This FOR..NEXT loop moves boths ships and keeps them inbounds
     FOR t = 1 TO 2
        Player(t).HorizPos = Player(t).HorizPos + Player(t).HorizDir
        IF Player(t).HorizPos < leftbound THEN Player(t).HorizPos = leftbound
        IF Player(t).HorizPos > rightbound THEN Player(t).HorizPos = rightbound
        Player(t).VertPos = Player(t).VertPos + Player(t).VertDir
        IF Player(t).VertPos < upbound THEN Player(t).VertPos = upbound
        IF Player(t).VertPos > downbound THEN Player(t).VertPos = downbound
     NEXT t

     ' If a one player game, computer determines next move when computer's
     '   ship reaches the boundaries.
     IF PlayrNum = 1 THEN
        IF Player(2).HorizPos < leftbound + 4 OR Player(2).HorizPos > rightbound - 4 THEN
                Player(2).HorizDir = -(Player(2).HorizDir)
        END IF
        IF Player(2).VertPos < upbound + 4 OR Player(2).VertPos > downbound - 4 THEN
                Player(2).VertDir = -(Player(2).VertDir)
        END IF
     END IF

     ' Redraw the ships in new coordinates
     PUT (Player(1).HorizPos, Player(1).VertPos), OnePlayr%, PSET
     PUT (Player(2).HorizPos, Player(2).VertPos), TwoPlayr%, PSET
END SUB

SUB ObjectScreen
    COLOR 15, 0   'Restore text and set foreground color to white
    CLS : Center 3, "Space War!"
    Center 4, "1987..1993 by Gary N. Wilkerson Jr."
    COLOR 14, 0: Center 6, "One Player Game"
    COLOR 11, 0: Center 8, "Shoot the enemy as many times as possible"
    Center 9, "before time expires."
    Center 10, "When time expires, you get bonus for"
    Center 11, "the number of times you hit the enemy"
    Center 12, "multiplied by the number of remaining lives times ten."
    COLOR 13, 0: Center 14, "Two Player Game"
    COLOR 11, 0: Center 16, "Two players battle it out until times runs out."
    Center 17, "The player with the most points, wins!!!"
    COLOR 15, 0: Center 19, "Press any key to continue"

    SparklePause
END SUB

' OnOff displays [ON] if condition (cond) is TRUE
'   otherwise it displays [OFF].
SUB OnOff (text$, cond)
     PRINT text$; " ";
     IF cond THEN PRINT "[ON]";  ELSE PRINT ; "[OFF]";
END SUB

SUB PlaySpWar
  Clock = 0
  DO WHILE Time > 0 OR (PlayrNum = 2 AND Player(1).Score = Player(2).Score)
    GetDirections
    MoveShips
    MoveBullets
    MoveAstrd

    PSET (RND * 640, 20 + RND * 180)    'Restore a star

    IF ShipShot THEN FindShip

    IF Astrd.Shot OR Astrd.Smash THEN
        Explode INT(Astrd.Horiz), INT(Astrd.Vert)
        IF NOT (Astrd.Smash) THEN TimeDsply 15
        Astrd.Shot = FALSE: Astrd.Smash = FALSE
    END IF

    ' Delay Game
    Rest .005

    ' End game if Player 1 runs out of lives
    IF PlayrNum = 1 AND Lives < 1 THEN Time = 0: EXIT SUB

    Clock = Clock + 1 AND 255

    IF Clock = 0 AND Astrd.TorF = FALSE THEN
        Astrd.TorF = TRUE: Astrd.Smash = FALSE: Astrd.Shot = FALSE
        Astrd.Horiz = RND * 640: Astrd.DirHoriz = RND * 4
        ' Use the random event generator to determine asteroid's direction
        IF RND * 2 >= 1 THEN Astrd.DirHoriz = -Astrd.DirHoriz
        IF RND * 2 >= 1 THEN
            Astrd.Vert = upbound: Astrd.DirVert = RND * 4
        ELSE
            Astrd.Vert = downbound: Astrd.DirVert = -RND * 4
        END IF
        IF SoundOn THEN PLAY "MBO0L16>ccce"
    END IF

    IF (Clock AND 15) = 0 THEN TimeDsply -1
  LOOP
END SUB

'Rest:
'  pauses the program
SUB Rest (t#)
  s# = TIMER
  t2# = MachSpeed * t# / SPEEDCONST
  DO
  LOOP UNTIL TIMER - s# > t2#
END SUB

' This procedure checks to see if either ship has crashed
' The GOTO's were necessary I'm afraid in order to make the game run faster
'   by not testing all the conditions unless a ship has crashed in any way.
FUNCTION ShipShot

     ShipShot = FALSE

     ' Check if bullet hit ships
     FOR t = 1 TO 2
        IF Player(t XOR 3).Shot THEN
                IF Player(t).HorizPos > Player(t XOR 3).ShotHoriz THEN GOTO missed
                IF Player(t).HorizPos + 40 < Player(t XOR 3).ShotHoriz THEN GOTO missed
                IF Player(t).VertPos > Player(t XOR 3).ShotVert THEN GOTO missed
                IF Player(t).VertPos + 30 < Player(t XOR 3).ShotVert THEN GOTO missed
                Player(t).Alive = FALSE: ShipShot = TRUE
                Player(t XOR 3).Shot = FALSE
missed: END IF
NEXT t

     'Check if ships hit each other
     IF Player(1).HorizPos < Player(2).HorizPos - 20 THEN GOTO safe
     IF Player(1).HorizPos > Player(2).HorizPos + 20 THEN GOTO safe
     IF Player(1).VertPos < Player(2).VertPos - 15 THEN GOTO safe
     IF Player(1).VertPos > Player(2).VertPos + 15 THEN GOTO safe
     FOR t = 1 TO 2: Player(t).Alive = FALSE: NEXT t: ShipShot = TRUE

safe:

     ' Check if ships hit asteroid
     FOR t = 1 TO 2
        IF Astrd.TorF THEN
                IF Player(t).HorizPos < Astrd.Horiz - 20 THEN GOTO clr
                IF Player(t).HorizPos > Astrd.Horiz + 20 THEN GOTO clr
                IF Player(t).VertPos < Astrd.Vert - 15 THEN GOTO clr
                IF Player(t).VertPos > Astrd.Vert + 15 THEN GOTO clr
                Astrd.TorF = FALSE: Astrd.Smash = TRUE
                ShipShot = TRUE: Player(t).Alive = FALSE
clr:    END IF
     NEXT t
END FUNCTION

' Sparklepause creates a flashing border for intro scenes
SUB SparklePause
     a$ = "*    *    *    *    *    *    *    *    *    *    *    *    *    *    *    *    *    "
     WHILE INKEY$ <> "": WEND
     WHILE INKEY$ = ""
     COLOR VAL(RIGHT$(TIME$, 2)) AND 15, 0
        FOR t = 1 TO 5
                LOCATE 1, 1: PRINT MID$(a$, t, 80);
                LOCATE 22, 1: PRINT MID$(a$, 6 - t, 80);
                FOR x = 2 TO 21
                        y = (t + x) MOD 5
                        IF y = 1 THEN b$ = "*" ELSE b$ = " "
                        LOCATE x, 80: PRINT b$;
                        LOCATE 23 - x, 1: PRINT b$;
                NEXT x
        NEXT t
     WEND
     COLOR 15, 0
END SUB

SUB TallyScore (Playr)
    ' Tally score according number of players
    IF PlayrNum = 1 THEN
        ' One Player game
        IF Playr = 1 THEN
            ' Player got shot
            DsplyScore 0, 0, -1
        ELSE
            ' Computer got shot
            ' Determine Score& by distance between ships
            y = Player(1).HorizPos - Player(2).HorizPos
            IF y >= 240 THEN
                Score& = 700    ' Long distance, sharp shooter
            ELSEIF y >= 140 AND y < 240 THEN
                Score& = 300    ' Safe distance, wimp
            ELSEIF y >= 90 AND y < 140 THEN
                Score& = 600    ' Moderate distance
            ELSEIF y >= 0 AND y < 90 THEN
                Score& = 900    ' Point blank, LOOKOUT!!!
            ELSE
                Score& = 100    ' Playing chicken, aren't we?
            END IF

            ' Display bonus score
            LOCATE Player(2).VertPos / 8 + 1, Player(2).HorizPos / 8 + 1
            PRINT Score&; : SLEEP 1
            LOCATE Player(2).VertPos / 8 + 1, Player(2).HorizPos / 8 + 1
            PRINT SPACE$(4);
            DsplyScore Score&, 0, 0
            NumbrHits = NumbrHits + 1
        END IF
    ELSE
        ' Two Player game
        IF Playr = 1 THEN DsplyScore 0, 1, 0 ELSE DsplyScore 1, 0, 0
    END IF
END SUB

' This SUB module adds assigned value to the time and displays it
SUB TimeDsply (cond)
     Time = Time + cond
     IF Time < 0 THEN EXIT SUB
     LOCATE 2, 32
     PRINT USING "Time ##:##"; INT(Time / 60); Time MOD 60
END SUB

FUNCTION Yes
     PRINT " (y/n)?";
     DO: a$ = UCASE$(INPUT$(1))
     LOOP UNTIL a$ = "Y" OR a$ = "N"
     IF a$ = "Y" THEN Yes = TRUE ELSE Yes = FALSE
END FUNCTION
