QBasic Rockwars
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

596 lines
16 KiB

'QBasic Rockwars v1.1, by Brennen Bearnes
'Version 1.1 has the following changes:
'1. A startup menu with the ability to change game settings.
'2. 3 different types of game.
'3. End screen now asks if you want to play again.
'4. A few small changes and optimizations (I hope).
'You play the part of the blue castle. You've been attacked by
'the red castle, who has started sending robotic tanks equipped with
'a short range death ray at you. Fortunately, you've been hard at work
'on your own secret weapon, a powerful catapult that can launch rocks
'all the way to the other side of the screen.
'How to play:
'A tank will start coming at you as soon as the game begins. If the
'red castle is fairly close, then you should shoot the tank first.
'Every time you blow up a tank, the red castle sends another one,
'so sooner or later, you'll have to hit the castle. To fire, press
'F1, and type in the angle (10-180) and velocity (0-100) you want. Be
'sure to take the wind into account. If the tank gets close enough,
'then you're toast.
'Brent P. Newhall did all the code for the explosions, as well as the star
'field, and a number of other small but effective changes.
'The little moon orbiting a planet in the background was done
'by Marco Koegler (and thanks to everyone else that told me how to do it).
'I figured out how to plot the projectiles from GORILLA.BAS, which I
'found on an old computer at school.
'Suggestions are welcome, and if you improve this in any way, please
'send me a copy.
'bbearnes@worldnet.att.net
'http://www.geocities.com/SiliconValley/Park/8994/
' Brent P. Newhall edited 9/30/96
' Notes: The explode code now restores the screen to how it looked prior
' to the explosion. I then added code to erase the bullet and
' exploded item just after the explosion. I also added the stars.
' Before, you could enter an angle of zero and just wipe out
' anything before you. Now the angle can be no less than 10.
' I've also cleaned up the code a bit, making it more readable,
' less time- and memory-intensive, etc.
'Thanks Brent!
DECLARE SUB newTank ()
DECLARE SUB drawTank ()
DECLARE SUB newRock ()
DECLARE SUB drawRock ()
DECLARE SUB explode (green%, blue%, red%, boomX, boomY, boomSize)
CONST pi=3.141592654#
TYPE arctype
Radius AS INTEGER
colr AS INTEGER
p1 AS SINGLE
p2 AS SINGLE
END TYPE
Restart:
'Variables that control the rock
DIM SHARED rockAngle
DIM SHARED rockSpeed
DIM SHARED startRockX
DIM SHARED startRockY
DIM SHARED oldRockX
DIM SHARED oldRockY
DIM SHARED Rock1Exist
DIM SHARED t 'time
DIM SHARED rockX
DIM SHARED rockY
DIM SHARED startRockXvel
DIM SHARED startRockYvel
DIM SHARED gravity
DIM SHARED wind
'The castles and tank.
DIM SHARED castle2X
DIM SHARED castleX
DIM SHARED TankExist
DIM SHARED TankX
DIM SHARED oldTankX
DIM SHARED tankSpeed
DIM SHARED castleExist
DIM SHARED Castle2Exist
DIM SHARED x1 AS INTEGER, y1 AS INTEGER, x2 AS INTEGER, y2 AS INTEGER
DIM SHARED TKills AS INTEGER, ShotsFired AS INTEGER, GameType AS INTEGER
DIM SHARED Castle2Hits AS INTEGER
DIM SHARED ExplodeHold(1 TO 10000) AS INTEGER
castleExist=1
Castle2Exist=1
SCREEN 13
GameType=1
menu:
CLS
PRINT TAB(10); "QBasic RockWars v1.1"
PRINT TAB(10); " By Brennen Bearnes"
PRINT
PRINT TAB(10); "1. Game Options"
PRINT TAB(10); "2. Start Game"
PRINT TAB(10); "3. Quit"
DO
LOCATE 7, 10: INPUT ">", menud
LOOP UNTIL menud >=1 AND menud <= 3
SELECT CASE menud
CASE 1
PRINT
INPUT "Gravity (9.8 Default): ", gravinput
PRINT
PRINT "Game type:"
PRINT "1. Red Castle can be destroyed,"
PRINT " unlimited tanks."
PRINT
PRINT "2. Red Castle cannot be destroyed,"
PRINT " limited tanks."
PRINT
PRINT "3. Red Castle takes several hits to"
PRINT " destroy, limited tanks."
PRINT
INPUT ">", GameType
IF GameType > 3 THEN GameType=3
IF GameType < 1 THEN GameType=1
GOSUB menu
CASE 2
PRINT
PRINT "While in the game:"
PRINT "F1 to fire"
PRINT "ESC exits"
PRINT
PRINT "Angles can be between 10 and 180."
PRINT "Velocity can be between 0 and 100."
PRINT "Remember to take the wind into account."
PRINT
PRINT "Enjoy!"
SLEEP
CASE 3
GOSUB Qwit
END SELECT
IF gravinput=0 THEN
gravity=9.8
ELSE
gravity=gravinput
END IF
'How many hits it takes to kill the red castle, based on GameType
IF GameType=1 THEN
Castle2Hits=1
ELSEIF GameType=2 THEN
Castle2Hits=1
ELSEIF GameType=3 THEN
Castle2Hits=3
END IF
CLS
'GET the player's castle.
DIM SHARED castle%(15, 15)
LINE (5, 5)-(20, 20), 1, BF
LINE (8, 5)-(10, 7), 0, BF
LINE (12, 5)-(14, 7), 0, BF
LINE (16, 5)-(18, 7), 0, BF
LINE (10, 15)-(15, 20), 0, BF
GET (5, 5)-(20, 20), castle%
CLS
'GET the computer's castle.
DIM SHARED castle2%(15, 15)
LINE (5, 5)-(20, 20), 4, BF
LINE (8, 5)-(10, 7), 0, BF
LINE (12, 5)-(14, 7), 0, BF
LINE (16, 5)-(18, 7), 0, BF
LINE (10, 15)-(15, 20), 0, BF
GET (5, 5)-(20, 20), castle2%
CLS
'GET the tank
DIM SHARED tank%(11, 8)
LINE (2, 4)-(10, 6), 2, BF
LINE (5, 1)-(8, 4), 2, BF
LINE (1, 2)-(5, 2), 2
CIRCLE (3, 6), 2, 2
CIRCLE (8, 6), 2, 2
GET (1, 1)-(11, 8), tank%
CLS
'The tank firing.
DIM SHARED tank2%(11, 8)
LINE (2, 4)-(10, 6), 2, BF
LINE (5, 1)-(8, 4), 2, BF
LINE (1, 1)-(5, 3), 2
CIRCLE (3, 6), 2, 2
CIRCLE (8, 6), 2, 2
GET (1, 1)-(11, 8), tank2%
CLS
'The center of the planet
planetX=180: planetY = 45
'Draw the planet
FOR circ%=1 TO 8
CIRCLE (planetX, planetY), circ%, 9
CIRCLE (planetX, planetY + 1), circ%, 9
NEXT circ%
LINE (planetX - 8, planetY)-(planetX + 8, planetY), 4
LINE (planetX - 8, planetY - 2)-(planetX + 8, planetY - 2), 12
LINE (planetX - 7, planetY - 3)-(planetX + 7, planetY - 3), 12
LINE (planetX - 5, planetY - 5)-(planetX + 5, planetY - 5), 4
LINE (planetX - 8, planetY + 3)-(planetX + 8, planetY + 3), 4
LINE (planetX - 6, planetY + 5)-(planetX + 6, planetY + 5), 4
CIRCLE (planetX + 2, planetY + 4), 2, 4
CIRCLE (planetX + 2, planetY + 4), 1, 4
CIRCLE (planetX + 3, planetY + 4), 1, 4
'Draw the ground
LINE (0, 175)-(320, 175), 10
PAINT (0, 176), 10, 1
'PUT the castles
castleX=15
PUT (castleX, 159), castle%
RANDOMIZE TIMER
castle2X=INT(RND * 175) + 175
IF castle2X >=285 THEN castle2X = 285
PUT (castle2X, 159), castle2%
'The code to make the moon "orbit" the planet was done by Marco Koegler
orbitRadius=30 'Radius of the orbit
moonAngle=0 'Initialize the moons angle
oldMoonX=planetX + 75: oldMoonY = planetY
'Display stars
FOR cnt%=1 TO 75
PSET (INT(RND * 319), INT(RND * 120) + 30), INT(RND * 15 + 15)
NEXT cnt%
'This is the main program loop.
DO
moonAngle=moonAngle + .5
IF moonAngle=360 THEN moonAngle = 0
theta=moonAngle * (pi / 180)
moonX=planetX + orbitRadius * (COS(theta))
moonY=planetY - orbitRadius * (SIN(theta))
'Erase old moon
CIRCLE (oldMoonX, oldMoonY), 2, 0
'Draw new moon
CIRCLE (moonX, moonY), 2, 8
oldMoonX=moonX: oldMoonY = moonY
ptime!=TIMER: WHILE ptime! = TIMER: WEND ' Pause
K$=INKEY$
IF LEN(K$)=2 THEN
SELECT CASE ASC(RIGHT$(K$, 1))
CASE 59 'F1
IF Rock1Exist=0 THEN
newRock
END IF
END SELECT
ELSEIF LEN(K$)=1 THEN
SELECT CASE ASC(K$)
CASE 27: quit=1
END SELECT
END IF
IF Rock1Exist=1 THEN
drawRock
END IF
IF TankExist=0 AND Castle2Exist = 1 THEN
newTank
ELSEIF TankExist=1 THEN
drawTank
END IF
'Check and see if the player has won or lost yet...
IF castleExist=0 THEN
'"Melt" the screen and end the game if the player loses.
quit=2
END IF
IF GameType=1 AND Castle2Exist = 0 AND TankExist = 0 THEN
quit=3
ELSEIF GameType=2 AND TKills >= 20 THEN
quit=4
ELSEIF GameType=3 THEN
IF TKills >=20 THEN
quit=4
END IF
IF Castle2Exist=0 AND TankExist = 0 THEN
quit=3
END IF
END IF
LOOP UNTIL quit > 0
'End of main program loop.
'The game now prints out your score at the end. Makes it seem like you've
'accomplished something. Or not.
'One shot is subtracted from ShotsFired if you destroy the red castle.
'seems only fair.
IF quit=2 THEN 'The ending if you lose.
'"Melt" the screen and end the game if the player loses.
LOCATE 8, 15: PRINT "you lose"
LOCATE 9, 14: PRINT "the end"
LOCATE 14, 10: PRINT "tanks killed: "; TKills
LOCATE 15, 10: PRINT "rocks fired: "; ShotsFired
IF TKills >=1 THEN
LOCATE 16, 10: PRINT "shots per kill: "; ShotsFired / TKills
END IF
SLEEP 2
DIM melt%(3000)
FOR RR=1 TO 3000
RANDOMIZE TIMER
XX=INT(RND * 271)
RANDOMIZE TIMER
YX=INT(RND * 150)
GET (XX, YX)-(XX + 48, YX + 48), melt%
PUT (XX, YX + 1), melt%, PSET
IF INKEY$=CHR$(27) THEN END
NEXT RR
ELSEIF quit=3 THEN
'The ending if you kill both the tank and castle (Game types 1 and 3).
LOCATE 13, 15: PRINT "you win"
LOCATE 14, 10: PRINT "tanks killed: "; TKills
LOCATE 15, 10: PRINT "rocks fired: "; ShotsFired
LOCATE 16, 10: PRINT "shots per kill: "; ShotsFired / TKills
ELSEIF quit=4 THEN 'The ending if you kill all the tanks.
LOCATE 12, 5: PRINT "The red castle has no tanks left."
LOCATE 13, 5: PRINT "Victory is yours."
LOCATE 15, 10: PRINT "tanks killed: "; TKills
LOCATE 16, 10: PRINT "rocks fired: "; ShotsFired
LOCATE 17, 10: PRINT "shots per kill: "; ShotsFired / TKills
END IF
WHILE INKEY$=""
WEND
CLS
'Ask player if they want to play again, then send them back to the starting
'menu.
INPUT "Do you want to play again"; menud$
menud$=UCASE$(menud$)
SELECT CASE menud$
CASE "Y"
quit=0
GOSUB Restart
CASE "N"
PRINT "Thanks for playing!"
GOSUB Qwit
END SELECT
Qwit:
END
SUB drawRock
oldRockX=rockX
oldRockY=rockY
'Calculate the next position of the rock
rockX=startRockX + (startRockXvel * t) + (.5 * (wind / 5) * t ^ 2)
rockY=startRockY + ((-1 * (startRockYvel * t)) + (.5 * gravity * t ^ 2)) * (200 / 350)
IF rockY >=174 THEN
'Do an explosion when the rock hits the "ground".
Rock1Exist=0
'The below code used to go in front of every explosion, 'til I figured out
'it would be easier to specify the variables when calling the SUB. I'm a
'little slow sometimes.
'green%=20
'blue%=0
'boomX=rockX
'boomY=rockY
'boomSize=6
explode 20, 0, red%, rockX, rockY, 6
CIRCLE (oldRockX, oldRockY), 1, 0 ' Erase rock
ELSEIF rockY >=159 AND rockX >= castle2X AND rockX <= castle2X + 15 THEN
Rock1Exist=0
CIRCLE (oldRockX, oldRockY), 1, 0 ' Erase rock
IF GameType <> 2 THEN
IF GameType=3 OR GameType = 1 THEN
Castle2Hits=Castle2Hits - 1 'Subtracts 1 hitpoint from castle.
END IF
'Do an explosion if the enemy castle has 0 hit points left
IF Castle2Hits=0 AND Castle2Exist = 1 THEN
ShotsFired=ShotsFired - 1 'Subtract 1 from the players shots fired.
Rock1Exist=0
Castle2Exist=0
explode 0, 0, red%, rockX, rockY, 50
PUT (castle2X, 159), castle2% ' Erase enemy castle
END IF
END IF
ELSEIF rockY >=167 AND rockX >= TankX AND rockX <= TankX + 10 THEN
'Do an explosion if rock hits the tank.
TKills=TKills + 1 'Add 1 to kill counter.
TankExist=0
Rock1Exist=0
explode 20, 0, red%, TankX, rockY, 30
CIRCLE (oldRockX, oldRockY), 1, 0 ' Erase rock
PUT (INT(TankX), 167), tank%, XOR ' Erase tank
END IF
IF rockX >=319 OR rockX <= 1 THEN
Rock1Exist=0
CIRCLE (oldRockX, oldRockY), 1, 0
END IF
IF Rock1Exist=1 THEN
CIRCLE (oldRockX, oldRockY), 1, 0
CIRCLE (rockX, rockY), 1, 12
ELSEIF Rock1Exist=0 THEN
rockX=0
rockY=0
END IF
t=t + .1
END SUB
SUB drawTank
'Move the tank, or if the tank is close enough, blow up the player's
'castle.
tankSpeed=.5
IF TankX > castleX + 60 THEN
'Make the tank try to avoid the rocks (doesn't work very well, but can
'make the game a little more challenging).
IF TankX - rockX < 20 AND TankX - rockX > -1 THEN
tankSpeed=-.8
END IF
oldTankX=TankX
TankX=TankX - tankSpeed
ELSEIF TankX <=castleX + 60 THEN
PUT (INT(TankX), 167), tank2%, PSET
SLEEP 1
PALETTE 95, 60
LINE (INT(TankX) - 2, 167)-(castleX + 15, 162), 95
FOR cnt%=1 TO 30
PALETTE 95, INT(RND * 30 + 30)
ptime!=TIMER: WHILE ptime! = TIMER: WEND ' Pause
NEXT cnt%
PALETTE 95, 0
TankExist=0
explode 0, 30, red%, castleX + 15, 162, 50
castleExist=0
END IF
PUT (INT(oldTankX), 167), tank%, XOR
PUT (INT(TankX), 167), tank%, PSET
END SUB
SUB explode (green%, blue%, red%, boomX, boomY, boomSize)
'This code started as EXPLODE.BAS by Brent P. Newhall. I've only made a
'few minor changes. The comments are all his.
''I was thinking about that "I need fire and an exploding graphic" post
''and came up with this simple explosion program. Any suggestions,
''comments, etc. are welcome with open arms.
''NOTES: There are two lines that do not wrap properly; you'll be able to
''find them easily. Also, NumArcs is set to 100, which does an
''acceptable explosion on my 486DX2/50 with 8 megs of RAM. You Pentium
''people should be able to get a much nicer explosion if you set NumArcs
''equal to 1000 or so.
''The program sets colors 100 to 131 to varying shades of red, orange,
''and yellow for the explosive effect, with the nice side-effect that
'you can theoretically do several explosions at once.
NumArcs=65
DIM arc(1 TO NumArcs) AS arctype
RANDOMIZE TIMER
FOR cnt%=1 TO NumArcs ' Initialise
arc(cnt%).Radius=0
arc(cnt%).p1=RND * 5.8
arc(cnt%).p2=arc(cnt%).p1 + (RND / 2)
arc(cnt%).colr=INT(RND * 5 + 100)
NEXT cnt%
red%=63 ' Start it deep red
FOR cnt%=100 TO 130 ' Change colors
PALETTE cnt%, red% + 256 * green% + 65536 * blue%
IF cnt% <=114 THEN ' Lower red, increase yellow
red%=red% - 2
green%=green% + 2
ELSE ' Lower red and yellow
red%=red% - 2
green%=green% - 2
END IF
NEXT cnt%
PALETTE 131, 0
TimeCnt%=0
x1=boomX - boomSize
IF x1 < 0 THEN x1=0
y1=boomY - boomSize
IF y1 < 0 THEN y1=0
x2=boomX + boomSize
IF x2 > 319 THEN x2=319
y2=boomY + boomSize
IF y2 > 199 THEN y2=199
GET (x1, y1)-(x2, y2), ExplodeHold(1)
DO
TimeCnt%=TimeCnt% + 1
FOR CurrArc%=1 TO NumArcs
IF arc(CurrArc%).Radius > 4 THEN ' Erase previous arc
CIRCLE (boomX, boomY), arc(CurrArc%).Radius - 5, 0, arc(CurrArc%).p1, arc(CurrArc%).p2
END IF
IF arc(CurrArc%).Radius=0 THEN ' Not yet alive
' About 30% of the time, create a new arc
IF RND > .7 THEN arc(CurrArc%).Radius=1
ELSEIF arc(CurrArc%).colr=131 THEN ' Dead
REM Do Nothing
ELSE
arc(CurrArc%).Radius=arc(CurrArc%).Radius + 1 ' Increase radius
arc(CurrArc%).colr=arc(CurrArc%).colr + 1 ' Increase color
' Draw arc
CIRCLE (boomX, boomY), arc(CurrArc%).Radius, arc(CurrArc%).colr, arc(CurrArc%).p1, arc(CurrArc%).p2
END IF
NEXT CurrArc%
t!=TIMER: WHILE t! = TIMER: WEND ' Pause
IF TimeCnt% >=boomSize THEN quitExplode = 1
LOOP UNTIL quitExplode > 0
PUT (x1, y1), ExplodeHold(1), PSET
END SUB
SUB newRock
'Add 1 to shots fired counter.
ShotsFired=ShotsFired + 1
'initialize the wind, and tell the player.
RANDOMIZE TIMER: windirec=RND * 2:
LOCATE 1, 1: PRINT SPC(30);
IF windirec <=1 THEN
RANDOMIZE TIMER: wind=RND * -10
LOCATE 1, 1: PRINT "wind: "; wind; " "
ELSEIF windirec > 1 THEN
RANDOMIZE TIMER: wind=RND * 10
LOCATE 1, 1: PRINT "wind:"; wind; " "
END IF
'Get player input for angle and velocity, and initialize the rock.
'Velocity can range from 0 to 100, and angle can be in between 10 and 180.
LOCATE 2, 1: PRINT "Angle: "
DO
LOCATE 2, 7: PRINT " ";
LOCATE 2, 7: INPUT "", rockAngle
LOOP UNTIL rockAngle >=10 AND rockAngle <= 180
LOCATE 3, 1: PRINT "Velocity: "
DO
LOCATE 3, 10: PRINT " ";
LOCATE 3, 10: INPUT "", rockSpeed
LOOP UNTIL rockSpeed >=0 AND rockSpeed <= 100
LINE (1, 1)-(50, 50), 0
startRockX=castleX + 6
startRockY=157
oldRockX=startRockX
oldRockY=startRockY
Rock1Exist=1
rockAngle=rockAngle / 180 * pi
startRockXvel=COS(rockAngle) * rockSpeed
startRockYvel=SIN(rockAngle) * rockSpeed
t=0
drawRock
END SUB
SUB newTank
'Initialize tank.
TankExist=1
TankX=castle2X - 12
oldTankX=castle2X - 12
PUT (castle2X, 159), castle2%, PSET
drawTank
END SUB