''''''''''''''' ' NewGame.bas ' ''''''''''''''' ' Written by Justin Richards (2012) ' Please use this file as a foundation for creating a new game. It puts the ' Structure of the game in place so you don't have to ask the question "Where ' do I begin??". All I ask is that you include me in the credits of your own ' creations if you do decide to use the code :) ' There is an Image Creator and Level Editor to go with this program which I ' have available also... ' Good Luck! '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Define a Graphics Type to use when reading records from the .gfx ' files we import. This is optional, you could just draw the graphics ' in game using LINE, CIRLCE and PAINT, etc commands instead, however ' in larger more complex games this will greatly increase the lines of ' code required and extend compile times. TYPE GraphicsType ' Always be descriptive with User Defined Type Names PX AS STRING * 2 ' Pixel X Co-ordinate PY AS STRING * 2 ' Pixel Y Co-ordinate PCO AS STRING * 3 ' Pixel Colour Value END TYPE ' One Image Record (consisting of a value for PX, PY, and PCO) will have a ' length of SEVEN. This value will be assigned to the variable GTIncrement ' and will be used to read the graphics file records sequentially later. DIM SHARED GTIncrement DIM SHARED GT AS GraphicsType ' GT is how we refer to our User Defined Type when opening our files ' Use "SHARED" when dimensioning variables if you plan on having multiple ' SUBS which will test for that value. It is a lot less confusing ' when calling SUBS and will allow for less human error. ' Dimension variable arrays for our graphics. Use a descriptive name for ' example, if the graphic is a Man facing Left then "ManL" would be a good ' name. You can clearly tell what the graphic will be by the name. ' The Number in brackets beside the name refers to how much memory the graphic ' will require. Below is a reference for the graphics used in this game... ' 10 x 10 pixels = (51) ' 20 x 20 pixels = (201) ' 30 x 30 pixels = (451) ' 40 x 40 pixels = (801) ' The formula is as follows - Width x Height (in pixels) / 2 + 1 ' Use this formula rather than guessing the numbers. Having large numbers ' in excess of what is required will waste memory. ' These are the Graphic Arrays for our Character. There are 22 frames in ' total. This is just an example, you could quite easily use more or less ' frames in your own animations. DIM SHARED ManL(801) AS INTEGER ' Standing, Walking and Crouching DIM SHARED ManLStep(801) AS INTEGER DIM SHARED ManLCrouch(801) AS INTEGER DIM SHARED ManLfi8(801) AS INTEGER ' Firing the Shotgun DIM SHARED ManLfi7(801) AS INTEGER DIM SHARED ManLfi6(801) AS INTEGER DIM SHARED ManLfi5(801) AS INTEGER DIM SHARED ManLCfi8(801) AS INTEGER ' Firing while crouching DIM SHARED ManLCfi7(801) AS INTEGER DIM SHARED ManLCfi6(801) AS INTEGER DIM SHARED ManLCfi5(801) AS INTEGER DIM SHARED ManR(801) AS INTEGER ' These are the same but facing right DIM SHARED ManRStep(801) AS INTEGER DIM SHARED ManRCrouch(801) AS INTEGER DIM SHARED ManRfi8(801) AS INTEGER DIM SHARED ManRfi7(801) AS INTEGER DIM SHARED ManRfi6(801) AS INTEGER DIM SHARED ManRfi5(801) AS INTEGER DIM SHARED ManRCfi8(801) AS INTEGER DIM SHARED ManRCfi7(801) AS INTEGER DIM SHARED ManRCfi6(801) AS INTEGER DIM SHARED ManRCfi5(801) AS INTEGER ' The door has a closed and open state, comprised of three images DIM SHARED DoorBottom(801) AS INTEGER DIM SHARED DoorTop(51) AS INTEGER DIM SHARED DoorOpen(51) AS INTEGER ' Grass is a Decal Image used to make the floors look more interesting. DIM SHARED Grass(201) AS INTEGER ' The Droid is going to move around the screen switching from left to right. DIM SHARED DroidLeft(201) AS INTEGER DIM SHARED DroidRight(201) AS INTEGER ' Crates will be placed on the screen for effect. DIM SHARED CrateWood(451) AS INTEGER ' These Rock Decals will be placed as foreground objects. DIM SHARED RockDecalBottom(451) AS INTEGER DIM SHARED RockDecalBottom1(451) AS INTEGER DIM SHARED RockDecalTop(451) AS INTEGER DIM SHARED RockDecalTop1(451) AS INTEGER ' These hearts form the health meter in the HUD. DIM SHARED SmallHeart(201) AS INTEGER DIM SHARED SmallHeart1(201) AS INTEGER DIM SHARED HalfHeart(201) AS INTEGER DIM SHARED HalfHeart1(201) AS INTEGER ' The Get Images SUB routine will capture our images and store them in the ' image arrays we defined earlier. DECLARE SUB GetImages ' The DrawImages SUB routine will be used to draw graphics onto the screen at ' different points during the game loop. The DrawWhat variable will define ' what to draw when the routine is called. It has been Dimensioned as a ' STRING so we can clearly see what to draw. It's values are as follows: ' "BackGround" - Draw the Background Tiles Only ' "FixedSprites" - Draw things that are fixed in position but not background objects ' "MovingSprites" - Draw Enemies and other items that change position ' "ForeGround" - Draw Decals in front of everything else DECLARE SUB DrawImages DIM SHARED DrawWhat AS STRING ' This SUB routine draws the Heads Up Display DECLARE SUB Hud ' GameTimer is a variable used to control fast animations. In our example it ' will range from -16 to 16 but could be altered to suit your needs. DIM SHARED GameTimer ' TimeCount measures One Second Delays. Our example will increment until it ' reaches 4 and then reset to 0. DIM SHARED TimeCount ' Secs (Seconds) and Mins (Minutes) will be used to make our level timer. DIM SHARED Secs, Mins ' The DrawMan SUB routine will draw the man :| . All Man variables are ' pre-fixed with with "Mn" so that we can tell them apart from other variables. DECLARE SUB DrawMan DIM SHARED MnX, MnY, MnSide ' X and Y Position and the direction he is facing DIM SHARED MnWalkOn, MnWalkTimer, MnCrouchOn, MnJumpOn ' Movement Status DIM SHARED MnFireTimer, MnFireOn ' Firing Status. DIM SHARED MnHealth, MnAmmo ' Health and Ammunition Status Variables ' Every object in the game (excluding the man) will be loaded in the form of ' a tile. ' Each Tile has several properties which will tell our DrawImages SUB routine ' how to draw the tile and also which tiles can be interacted with by the ' player. There are currently 30 tiles per screen, this can be modified. DIM SHARED TileType(30) DIM SHARED TileProperty1(30) DIM SHARED TileProperty2(30) DIM SHARED TileColour1(30) DIM SHARED TileColour2(30) DIM SHARED TileX1(30) DIM SHARED TileY1(30) DIM SHARED TileX2(30) DIM SHARED TileY2(30) ' Always include an Error Handler in your programs even if it does just end ' the program. That's far better than it catching an exception and scaring ' the user into thinking they have crashed their machine. ON ERROR GOTO Errors ' Change working directory to where the game files are stored. Note, this ' will cause an error if the directory doesn't exist. CHDIR "NewGame" ' Here we call the GetImages SUB routine which will load all the game images ' we require for use throughout the game. GTIncrement = 7 ' The length of our Graphics Type (defined at the beggining) GetImages ' A reference point for the start of the game. Here we can reset variables ' and restart the program without having to physically quit and open it again. ' Its main use would be starting a new game or loading a game. Start: ' Set up Game Variables ' Health MnHealth = 290 ' 315 = dead / 275 = full, increment -5 for half a heart ' Ammunition MnAmmo = 60 ' 90 = full / 0 = empty, increment 3 for one shell ' Timers GameTimer = -1 ' GameTimer can never equal zero or the animations will freeze TimeCount = 1 ' Reset all 30 tiles to -1 values (don't exist) before we modify them. FOR ResetTile = 0 TO 29 TileType(ResetTile) = -1 TileProperty1(ResetTile) = -1 TileProperty2(ResetTile) = -1 TileColour1(ResetTile) = -1 TileColour2(ResetTile) = -1 TileX1(ResetTile) = -1 TileY1(ResetTile) = -1 TileX2(ResetTile) = -1 TileY2(ResetTile) = -1 NEXT ResetTile ' Call the gsGetTiles GOSUB routine to create the game objects. GOSUB gsGetTiles ' Place our Man into the level and give him health and ammunition MnX = 60 MnY = 50 MnSide = 1 MnHealth = 270 MnAmmo = 60 ' Keyboard Capture Code... ' The following codes refer to the machine code for each key on the keyboard. ' By storing these codes into appropriately named constant variables we can ' easily test them without confusion. CONST Key_Up& = 18432 CONST Key_Down& = 20480 CONST Key_Left& = 19200 CONST Key_Right& = 19712 CONST Key_Space& = 32 CONST Key_Enter& = 13 CONST Key_Ctrl& = 100306 CONST Key_Shift& = 100304 CONST Key_KP_Zero& = 48 ' Set OrigTime$ Variable to the current system time (TIME$). OrigTime$ = TIME$ ' The following variable TimeSet will determine how fast the game runs. Play ' around with it and see what happens. TimeSet = 1 ' Reset this variable to 0. It needs to equal TimeSet in order for the game ' loop to process. It will be incremented by 1 at the end of the main game ' loop. GameSpeed = 0 ' Now for the MAIN GAME LOOP!!!! DO ' Clear a space in memory to store the timer ticks using DEF SEG. DEF SEG = 0 ' Reset the Timer Tick to 0 using POKE. POKE 1132, 0 ' If the desired delay has passed then process the main game loop... IF GameSpeed = TimeSet THEN ' Clear the Keyboard buffer and end game if esacpe key is pressed FOR KbdLoop = 0 TO 32 ' The buffer remembers only 32 keystrokes. Kbd$ = "" ' Reset Kbd$ to nothing so that it can be reused. ' By setting Kbd$ to INKEY$ we take one keystroke away from the keyboard ' buffer, once we have done this 32 times the buffer will be empty. Kbd$ = INKEY$ IF Kbd$ = CHR$(27) THEN ' CHR$(27) is the character code for 'Escape' ' Clear the screen then display the blank screen, pause for a moment ' and disable full screen mode, then return to the OS. CLS _DISPLAY SLEEP 1 _FULLSCREEN _OFF SYSTEM END IF NEXT KbdLoop ' Increment the GameTimer Variable IF GameTimer > 0 THEN GameTimer = GameTimer + 1 IF GameTimer < 0 THEN GameTimer = GameTimer - 1 ' Reset the GameTimer Variable if it gets to large. 16 works quite well ' but can be changed to suit longer or shorter animation requirements. IF GameTimer < -16 THEN GameTimer = 1 ELSEIF GameTimer > 16 THEN GameTimer = -1 END IF ' Increment TimeCount if one second has passed and calculate the seconds ' and minutes that have passed since beginning the game. Note that if the ' time goes passed midnight it will reset to 0:00:00 and the calculations ' wont work which will stop the animations also. There is a fix for this, ' it's so infrequent that I never bothered with it. IF TIME$ > OrigTime$ THEN TimeCount = TimeCount + 1 ' Reset OrigTime so we can wait for another second to pass OrigTime$ = TIME$ ' If we reach 10 minutes then stop counting... IF NOT (Mins = 9 AND Secs = 59) THEN Secs = Secs + 1 ' After a minute, reset seconds to zero and increment minutes by one. IF Secs = 60 THEN Secs = 0 Mins = Mins + 1 END IF END IF ' TimeCount can be reset at any stage, but in this example we will use 4. ' 4 means you can have 4 one second animations (eg an item spinning around ' using 4 animations will end up back where it started when we reset ' TimeCount). IF TimeCount = 4 THEN OrigTime$ = TIME$ TimeCount = 0 END IF ' Determine which keys are being pressed. _KEYDOWN checks our constant ' codes from before and returns -1 if the key is being pressed. Using ' these newly created variables we can now test acurately for keyboard ' input. UpOn = _KEYDOWN(Key_Up&) DownOn = _KEYDOWN(Key_Down&) LeftOn = _KEYDOWN(Key_Left&) RightOn = _KEYDOWN(Key_Right&) SpaceOn = _KEYDOWN(Key_Space&) EnterOn = _KEYDOWN(Key_Enter&) CtrlOn = _KEYDOWN(Key_Ctrl&) ShiftOn = _KEYDOWN(Key_Shift&) ZeroOn = _KEYDOWN(Key_KP_Zero&) ' Draw the Background, Floors and Walls Only... DrawWhat$ = "BackGround" DrawImages ' Test the area around the Man to determine whether he should be able to ' Jump or Walk, or whether he should be falling down or not... The POINT ' Command gets the colour value of the X and Y co-ordinates you are testing. ' We can then determine what should happen based on the colour returned. MPointLeft = POINT(MnX - 25, MnY) MPointRight = POINT(MnX + 24, MnY) MPointFallLeft = POINT(MnX - 20, MnY + 39) MPointFallRight = POINT(MnX + 19, MnY + 39) MPointJumpLeft = POINT(MnX - 15, MnY + 5) MPointJumpRight = POINT(MnX + 14, MnY + 5) IF MnSide = 0 THEN MPointJumpTop = POINT(MnX + 10, MnY - 15) IF MnSide = 1 THEN MPointJumpTop = POINT(MnX - 9, MnY - 15) MPointGroundLeft = POINT(MnX - 11, MnY + 40) MPointGroundRight = POINT(MnX + 10, MnY + 40) MPointGroundBottom = POINT(MnX, MnY + 40) ' Now that we have tested for collisions we can draw the more complex ' images onto the screen starting with the Fixed Sprites... DrawWhat$ = "FixedSprites" DrawImages ' Reposition the Man if he has collided with a floor or wall. ' The MnX and MnY test are to make sure the Man is on the screen when we ' test. The MPoint tests are to establish if there is ground below or ' ceiling above and we test differently depending on MnSide. If these ' conditions are met, we move the man 20 pixels in either direction. IF (MnX > 20 AND NOT MnX > 300) AND MnY > 0 AND MnY < 160 AND (MPointFallLeft <> 8 OR MPointJumpLeft <> 8) AND MnSide = 0 AND MnY < 170 THEN MnX = MnX + 20 IF (MnX < 300 AND NOT MnX < 20) AND MnY > 0 AND MnY < 160 AND (MPointFallRight <> 8 OR MPointJumpRight <> 8) AND MnSide = 1 AND MnY < 170 THEN MnX = MnX - 20 ' Make the Man fall down ' Test the ground beneath the man if he is not already falling or jumping. IF (MPointGroundLeft = 8 AND MPointGroundRight = 8 AND MPointGroundBottom = 8 OR MnY > 150) AND MnFallOn = 0 AND MnJumpOn = 0 THEN MnFallOn = 1 ' Increment MnFallOn until the desired delay has passed. IF MnFallOn > 0 THEN MnFallOn = MnFallOn + 1 ' Once it reaches 4 we make him fall, a smaller number will make him fall ' faster, and vice versa. IF MnFallOn = 4 THEN ' Reset the fall variable so that we can perform the test again. MnFallOn = 0 ' Test for ground if the man is on the top level or bottom level only. IF (MPointGroundLeft <> 8 OR MPointGroundRight <> 8) AND (MnY = 140 OR MnY = 50) THEN ' If ground is found then the man's Y position should remain the same. ELSE ' If no ground is found then we move the man down by 10 pixels MnY = MnY + 10 END IF ' This line is optional, if the man falls of the bottom of the screen ' he will re-appear at the top of the screen, comment out to stop this ' but be sure to add code to kill the man if he does go off the screen ' otherwise he will fall forever. IF MnY > 200 THEN MnY = 0 END IF ' Make the Man Fire his Weapon ' If you have ammo and you press the spacebar and you are not already ' firing, walking or falling then we make him fire. IF SpaceOn = -1 AND MnAmmo > 0 AND MnFireTimer = 0 AND MnWalkOn = 0 AND MnFallOn = 0 THEN ' Take One Shell away MnAmmo = MnAmmo - 3 ' Set this to 8 to start the firing animation MnFireTimer = 8 ' This is used to delay the animation, once it reaches 4 we move to the ' next animation. MnFireOn = 1 END IF ' Increment the Firing Animation Timer IF MnFireOn > 0 THEN MnFireOn = MnFireOn + 1 ' If the desired delay has passed then make him fire. IF MnFireTimer > 0 AND MnFireOn = 4 THEN ' Reduce the Fire Timer until it returns to 0 MnFireTimer = MnFireTimer - 1 ' If the animation is still going then continue to reset the delay timer IF MnFireTimer > 0 THEN MnFireOn = 1 IF MnFireTimer = 0 THEN MnFireOn = 0 END IF ' At this point we test the shells path and draw the bullet flash IF MnFireOn = 3 AND MnFireTimer = 7 THEN ' Is the man facing left or right? SELECT CASE MnSide CASE 0 ' Left ' Test from the gun barrel till the end of the screen until we find ' something other than grey space. FOR fx = MnX - 19 TO 0 STEP -2 IF MnCrouchOn = 0 THEN fy = MnY + 19 ' Standing Test IF MnCrouchOn = 1 THEN fy = MnY + 29 ' Crouching Test fPoint = POINT(fx, fy) ' What colour is behind the bullet? ' If its grey space then draw a yellow dot IF fPoint = 8 THEN PSET (fx, fy), 43 ELSE ' If not then skip to the end and stop testing fx = 0 END IF NEXT fx CASE 1 ' Right ' Test from the gun barrel till the end of the screen until we find ' something other than grey space. FOR fx = MnX + 19 TO 319 STEP 2 IF MnCrouchOn = 0 THEN fy = MnY + 19 ' Standing Test IF MnCrouchOn = 1 THEN fy = MnY + 29 ' Crouching Test fPoint = POINT(fx, fy) ' What colour is behind the bullet? ' If its grey space then draw a yellow dot IF fPoint = 8 THEN PSET (fx, fy), 43 ELSE ' If not then skip to the end and stop testing fx = 319 END IF NEXT fx END SELECT END IF ' Move the Man ' If you push left, without pushing right and there are no obstacles in ' front of you and you are also within range of the screen then walk... IF LeftOn = -1 AND RightOn = 0 AND MnWalkOn = 0 AND (MPointLeft = 8 OR MnX <= 20 OR MnX > 300) THEN MnWalkOn = -1 END IF ' If you push right, without pushing left and there are no obstacles in ' front of you and you are also within range of the screen then walk... IF RightOn = -1 AND LeftOn = 0 AND MnWalkOn = 0 AND (MPointRight = 8 OR MnX >= 300 OR MnX < 20) THEN MnWalkOn = 1 END IF ' Increment MnWalkTimer to create the desired animation delay. IF MnWalkOn <> 0 THEN MnWalkTimer = MnWalkTimer + 1 ' Once the delay has passed we then call gsManMover to move the man IF MnWalkOn <> 0 AND MnWalkTimer > 2 THEN GOSUB gsManMover ' If the man walks off the screen we want him to re-appear on the other ' side (not normally, but for this demo we do...) IF MnX <= -20 AND MnY > 50 THEN MnX = MnX + 340 MnY = MnY - 90 END IF IF MnX >= 340 AND MnY <= 50 THEN MnX = MnX - 340 MnY = MnY + 90 END IF ' Crouching ' If you push down and you aren't already crouching, walking or jumping ' then make the Man crouch. IF DownOn = -1 AND MnCrouchOn = 0 AND MnWalkTimer = 0 AND MnJumpOn = 0 AND CrouchTime = 0 THEN MnCrouchOn = 1 ' CrouchTime is set to 3 after you complete one walk animation. You can ' only crouch if CrouchTime is 0. This stops the man crouching for a ' split second if you hold the down key and move left or right. IF CrouchTime > 0 THEN CrouchTime = CrouchTime - 1 ' If you stop pushing down or start walking or jumping then you will stop ' crouching. IF (DownOn = 0 OR MnWalkTimer <> 0 OR MnJumpOn = 1) AND MnCrouchOn = 1 THEN MnCrouchOn = 0 ' Jumping ' If you press up and you aren't already jumping or falling and your ' standing on the ground with no obstacles above then make the man Jump! IF UpOn = -1 AND MnJumpOn = 0 AND (MnY = 140 OR MnY = 50) AND MnFallOn = 0 AND MPointJumpTop = 8 THEN MnJumpOn = 1 ' If your jumping then increment JumpTimer until the desired delay is ' reached then reset it and call the MnJump Routine. IF MnJumpOn = 1 THEN JumpTimer = JumpTimer + 1 IF JumpTimer > 3 THEN JumpTimer = 0 GOSUB MnJump END IF END IF ' Now we can draw the interactive sprites. DrawWhat$ = "MovingSprites" DrawImages ' Its now time to draw the Man, foreground objects and the HUD DrawMan ' Calls the SUB to draw the man based on his status ' The foreground images are placed in front of the man, aswell as the HUD DrawWhat$ = "ForeGround" DrawImages Hud ' Calls the SUB to draw the heads up display. ' Reset this variable in order to maintain a constant game speed. If you ' don't the game will display once and then freeze :( GameSpeed = 0 ' Having the _DISPLAY command inside a loop disables the auto display ' which happens as standard. This means that nothing that we have drawn ' onto the screen has been displayed so far, its all been drawn into ' memory, ready to be copied to the screen once the loop reaches this ' command. _DISPLAY END IF ' Once we have completed one cycle of the main game loop we must then pause ' until the clock ticks have reached aproximately 1/18th of a second since ' we started the loop (this is how we maintain the speed, we are testing ' an external time source instead of variables within the game). DO ' Delay for 1/18th of a second LOOP UNTIL PEEK(1132) >= 1 DEF SEG ' reset segment to default ' GameSpeed is incremented and then tested at the beginning of the loop. GameSpeed = GameSpeed + 1 LOOP Errors: ' Using SYSTEM instead of END will return the user to the OS without having to ' press any key to continue. SYSTEM '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Thats it, Game Over :) ' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' gsManMover: SELECT CASE MnWalkOn CASE IS > 0 ' Walking Right ' If you fire your weapon and then take a step backwards you will not ' turn around but instead remain facing your target. IF MnFireTimer = 0 THEN MnSide = 1 ' Turn around if not firing your gun. ELSE ' Test to see if you are going to collide with other objects and then ' reposition the man accordingly. IF MPointRight <> 8 THEN MnX = MnX - 20 END IF ' Move man right, 5 pixels at a time. Increment the animation, and reset ' it if it reaches the 5th rotation. MnX = MnX + 5 MnWalkOn = MnWalkOn + 1 IF MnWalkOn = 5 THEN MnWalkOn = 0 CASE IS < 0 ' Walking Left ' If you fire your weapon and then take a step backwards you will not ' turn around but instead remain facing your target. IF MnFireTimer = 0 THEN MnSide = 0 ' Turn around if not firing your gun. ELSE ' Test to see if you are going to collide with other objects and then ' reposition the man accordingly. IF MPointLeft <> 8 THEN MnX = MnX + 20 END IF ' Move man left, 5 pixels at a time. Increment the animation, and reset ' it if it reaches the 5th rotation. MnX = MnX - 5 MnWalkOn = MnWalkOn - 1 IF MnWalkOn = -5 THEN MnWalkOn = 0 END SELECT ' Reset the Delay Variables... MnWalkTimer = 0 CrouchTime = 3 RETURN MnJump: ' Here we move the Man up and down in an arc like fashion depending on how ' long it has been since you started jumping. SELECT CASE MJT ' Stands for Man Jump Timer CASE 0 MnY = MnY - 10 CASE 1 MnY = MnY - 7 CASE 2 MnY = MnY - 5 CASE 3 MnY = MnY - 2 CASE 4 MnY = MnY - 1 CASE 5, 6 CASE 7 MnY = MnY + 1 CASE 8 MnY = MnY + 2 CASE 9 MnY = MnY + 5 CASE 10 MnY = MnY + 7 CASE 11 MnY = MnY + 10 ' The jump has now been completed and the variables are reset. MJT = -1 JumpTimer = 0 MnJumpOn = 0 END SELECT ' Increment the timer... MJT = MJT + 1 RETURN gsGetTiles: ' Normally in larger games you would read these values from a level file of ' some sort rather than create them in code, however for this example we only ' have one screen to display so we will just create each tile manually. ' To do this we need to define a Type, some Properties, Colours and Position ' for each tile so that the DrawImages SUB routine can arrange them on the ' screen appropriately. ' Create a floor tile with straight sides, in two shades of brown, starting ' from x(-20), y(180) and finishing at x(339), y(199). TileType(0) = 0 ' 0 for Floor TileProperty1(0) = 0 ' 0 for Straight edges TileColour1(0) = 6 ' 6 for Standard Brown Colour TileColour2(0) = 42 ' 42 for Light Brown Colour TileX1(0) = -20 ' The Co-ordinates from above TileY1(0) = 180 TileX2(0) = 339 TileY2(0) = 199 ' There, done, the first tile has been created, now to make some more floors TileType(1) = 0 ' 0 for Floor TileProperty1(1) = 2 ' 2 for Slanted edges TileColour1(1) = 6 ' 6 for Standard Brown Colour TileColour2(1) = 42 ' 42 for Light Brown Colour TileX1(1) = -20 ' In the middle on the left hand side TileY1(1) = 90 TileX2(1) = 119 TileY2(1) = 109 TileType(2) = 0 ' 0 for Floor TileProperty1(2) = 2 ' 2 for Slanted edges TileColour1(2) = 6 ' 6 for Standard Brown Colour TileColour2(2) = 42 ' 42 for Light Brown Colour TileX1(2) = 200 ' In the middle on the Right hand side TileY1(2) = 90 TileX2(2) = 339 TileY2(2) = 109 TileType(3) = 0 ' 0 for Floor TileProperty1(3) = 0 ' 0 for Straight edges TileColour1(3) = 6 ' 6 for Standard Brown Colour TileColour2(3) = 42 ' 42 for Light Brown Colour TileX1(3) = -20 ' A roof for the room TileY1(3) = 0 TileX2(3) = 339 TileY2(3) = 19 ' Now for some walls... TileType(4) = 1 ' 1 for Wall TileProperty1(4) = 2 ' 2 for Slanted edges TileColour1(4) = 6 ' 6 for Standard Brown Colour TileColour2(4) = 42 ' 42 for Light Brown Colour TileX1(4) = -20 ' Top Left Hand Side TileY1(4) = 20 TileX2(4) = 39 TileY2(4) = 89 TileType(5) = 1 ' 1 for Wall TileProperty1(5) = 1 ' 1 for Rounded edges TileColour1(5) = 6 ' 6 for Standard Brown Colour TileColour2(5) = 42 ' 42 for Light Brown Colour TileX1(5) = 280 ' Bottom Right Hand Side TileY1(5) = 110 TileX2(5) = 339 TileY2(5) = 179 TileType(6) = 1 ' 1 for Wall TileProperty1(6) = 1 ' 1 for Rounded edges TileColour1(6) = 6 ' 6 for Standard Brown Colour TileColour2(6) = 42 ' 42 for Light Brown Colour TileX1(6) = 220 ' Bottom Right Hand Cieling TileY1(6) = 110 TileX2(6) = 299 TileY2(6) = 129 TileType(7) = 1 ' 1 for Wall TileProperty1(7) = 1 ' 1 for Rounded edges TileColour1(7) = 6 ' 6 for Standard Brown Colour TileColour2(7) = 42 ' 42 for Light Brown Colour TileX1(7) = 20 ' Top Left Hand Cieling TileY1(7) = 20 TileX2(7) = 99 TileY2(7) = 39 ' And now some Droids... TileType(8) = 14 ' 14 for Droid TileProperty1(8) = 10 ' 10 hit points TileProperty2(8) = 0 ' 0 to spawn facing left TileX1(8) = 60 ' Bottom Floor TileY1(8) = 160 TileX2(8) = 79 TileY2(8) = 179 TileType(9) = 14 ' 14 for Droid TileProperty1(9) = 10 ' 10 hit points TileProperty2(9) = 1 ' 1 to spawn facing right TileX1(9) = 160 ' Bottom Floor TileY1(9) = 160 TileX2(9) = 179 TileY2(9) = 179 ' And a Crate or two for good luck... TileType(10) = 15 ' 15 for Crate TileProperty1(10) = 10 ' 10 hit points TileProperty2(10) = 0 ' 0 to spawn as a wooden crate TileX1(10) = 240 ' Bottom Floor TileY1(10) = 150 TileX2(10) = 279 TileY2(10) = 179 TileType(11) = 15 ' 15 for Crate TileProperty1(11) = 10 ' 10 hit points TileProperty2(11) = 0 ' 0 to spawn as a wooden crate TileX1(11) = 205 ' Bottom Floor TileY1(11) = 150 TileX2(11) = 234 TileY2(11) = 179 ' For instant awesome, just add rocks... TileType(12) = 16 ' 16 for Decal TileProperty1(12) = 2 ' 2 for Rocks TileProperty2(12) = 0 ' 0 for Bottom Left Side TileX1(12) = 20 ' Bottom Floor TileY1(12) = 155 TileX2(12) = 49 TileY2(12) = 184 TileType(13) = 16 ' 16 for Decal TileProperty1(13) = 2 ' 2 for Rocks TileProperty2(13) = 1 ' 1 for Bottom Right Side TileX1(13) = 255 ' Bottom Floor TileY1(13) = 155 TileX2(13) = 284 TileY2(13) = 184 TileType(14) = 16 ' 16 for Decal TileProperty1(14) = 2 ' 2 for Rocks TileProperty2(14) = 2 ' 2 for Top Left Side TileX1(14) = 35 ' Top Floor TileY1(14) = 35 TileX2(14) = 64 TileY2(14) = 64 TileType(15) = 16 ' 16 for Decal TileProperty1(15) = 2 ' 2 for Rocks TileProperty2(15) = 0 ' 0 for Bottom Left Side TileX1(15) = 30 ' Top Floor TileY1(15) = 65 TileX2(15) = 59 TileY2(15) = 94 ' And a door to finish things off... TileType(16) = 2 ' 2 for Door TileProperty1(16) = 0 ' 0 for Unlocked TileColour1(16) = 6 ' 6 for Standard Brown Colour TileColour2(16) = 42 ' 42 for Light Brown Colour TileX1(16) = 220 ' Top Floor TileY1(16) = 20 TileX2(16) = 239 TileY2(16) = 89 ' If you are wondering why the tile type numbers are out of order its because ' the tile definitions are ripped straight from another game of mine and I'm ' only using a few of the available tiles in this demo to show the basic ' method of level building. RETURN SUB GetImages ' This SUB routine converts the .gfx (text) files into images and stores them ' into easily called upon variable arrays. ' Set the screen dimensions and colours. the ", , 1, 0" uses screen page 1 ' instead of the default 0 meaning we will not see any drawing on the screen ' until we switch screen pages. This looks much more professional than having ' a bunch of images flash up on the screen and disappear suddenly. ' In this example we are using a screen mode identical to the old Qbasic ' Screen Mode 13. 320 pixels wide x 200 pixels high and with 256 colours. The ' difference is that we can use a screen page in QB64 where that was not ' possible in the original Qbasic program. SCREEN _NEWIMAGE(320, 200, 256), , 1, 0 ' This sets the window to full screen mode, comment out if you prefer a ' normal window instead. _FULLSCREEN ' This part of the SUB opens each individual Image file one by one, draws it ' on the screen, then captures it and stores it as a variable array. Broken ' down it works like this... ' Open the File we need located in the Resources Directory and read each ' record using the length defined by GT. OPEN "Resources\ManL.gfx" FOR BINARY AS 1 LEN = LEN(GT) ' Run the GetLoop GOSUB procedure to read the file and draw its contents ' onto the screen. GOSUB GetLoop ' Use the graphics statement "GET" to capture the displayed image and store it ' in the matching variable array. (0, 0)-(39, 39) captures an image 40 pixels ' wide by 40 pixels high. This will change depending on the image. GET (0, 0)-(39, 39), ManL() ' Close the file so that we can open another. CLOSE #1 ' Now repeat this for all remaining images. OPEN "Resources\ManLStep.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(39, 39), ManLStep() CLOSE #1 OPEN "Resources\ManLCrouch.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(39, 39), ManLCrouch() CLOSE #1 OPEN "Resources\ManLfi8.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(39, 39), ManLfi8() CLOSE #1 OPEN "Resources\ManLfi7.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(39, 39), ManLfi7() CLOSE #1 OPEN "Resources\ManLfi6.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(39, 39), ManLfi6() CLOSE #1 OPEN "Resources\ManLfi5.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(39, 39), ManLfi5() CLOSE #1 OPEN "Resources\ManLCfi8.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(39, 39), ManLCfi8() CLOSE #1 OPEN "Resources\ManLCfi7.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(39, 39), ManLCfi7() CLOSE #1 OPEN "Resources\ManLCfi6.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(39, 39), ManLCfi6() CLOSE #1 OPEN "Resources\ManLCfi5.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(39, 39), ManLCfi5() CLOSE #1 OPEN "Resources\ManR.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(39, 39), ManR() CLOSE #1 OPEN "Resources\ManRStep.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(39, 39), ManRStep() CLOSE #1 OPEN "Resources\ManRCrouch.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(39, 39), ManRCrouch() CLOSE #1 OPEN "Resources\ManRfi8.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(39, 39), ManRfi8() CLOSE #1 OPEN "Resources\ManRfi7.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(39, 39), ManRfi7() CLOSE #1 OPEN "Resources\ManRfi6.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(39, 39), ManRfi6() CLOSE #1 OPEN "Resources\ManRfi5.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(39, 39), ManRfi5() CLOSE #1 OPEN "Resources\ManRCfi8.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(39, 39), ManRCfi8() CLOSE #1 OPEN "Resources\ManRCfi7.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(39, 39), ManRCfi7() CLOSE #1 OPEN "Resources\ManRCfi6.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(39, 39), ManRCfi6() CLOSE #1 OPEN "Resources\ManRCfi5.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(39, 39), ManRCfi5() CLOSE #1 OPEN "Resources\Grass.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(19, 19), Grass() CLOSE #1 OPEN "Resources\DroidLeft.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(19, 19), DroidLeft() CLOSE #1 OPEN "Resources\DroidRight.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(19, 19), DroidRight() CLOSE #1 OPEN "Resources\RockDecalBottom.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(29, 29), RockDecalBottom() CLOSE #1 OPEN "Resources\RockDecalBottom1.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(29, 29), RockDecalBottom1() CLOSE #1 OPEN "Resources\RockDecalTop.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(29, 29), RockDecalTop() CLOSE #1 OPEN "Resources\RockDecalTop1.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(29, 29), RockDecalTop1() CLOSE #1 OPEN "Resources\SmallHeart.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(19, 19), SmallHeart() CLOSE #1 OPEN "Resources\SmallHeart1.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(19, 19), SmallHeart1() CLOSE #1 OPEN "Resources\HalfHeart.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(19, 19), HalfHeart() CLOSE #1 OPEN "Resources\HalfHeart1.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(19, 19), HalfHeart1() CLOSE #1 OPEN "Resources\CrateWood.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(29, 29), CrateWood() CLOSE #1 OPEN "Resources\DoorBottom.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(39, 39), DoorBottom() CLOSE #1 OPEN "Resources\DoorTop.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(9, 9), DoorTop() CLOSE #1 OPEN "Resources\DoorOpen.gfx" FOR BINARY AS 1 LEN = LEN(GT) GOSUB GetLoop GET (0, 0)-(9, 9), DoorOpen() CLOSE #1 ' Now that we are finished we can switch screen pages and start drawing to the ' screen in the main game loop and exit the SUB routine. SCREEN _NEWIMAGE(320, 200, 256), , 0, 0 EXIT SUB GetLoop: ' This looks trickier than it is... ' First we find the end of the file and store it as LoopIncrement LoopIncrement = LOF(1) ' LOF stands for Last of File and 1 is our open file. ' Now we read the last record (End of File, - 1 Record Length, + 1) GET #1, LoopIncrement - GTIncrement + 1, GT ' Work out how large the image is by reading the last X and Y Co-Ordinates PointX = VAL(GT.PX) PointY = VAL(GT.PY) ' LoopIncrement is set to -GTIncrement so that when the loop starts and ' GTIncrement is added back in, the records will start at 0. LoopIncrement = -GTIncrement ' This loop starts at co-ordinate 0, 0 and works its way to 39, 39 (in the ' first case). PointX and PointY were determined earlier. FOR DrawY = 0 TO PointY FOR DrawX = 0 TO PointX ' Read the current record values GET #1, LoopIncrement + GTIncrement + 1, GT ' We are only interested in the colour value which we will set to PointCo. ' We need to use the VAL function because GT.PCO is a STRING and we need ' to convert it to an INTEGER. PointCo = VAL(GT.PCO) ' Now that we have an X, Y and Colour value we can draw the pixel on the ' screen using PSET. PSET (DrawX, DrawY), PointCo ' We now increase LoopIncrement by GTIncrement to move to the next record ' and continue looping until the last pixel is drawn. LoopIncrement = LoopIncrement + GTIncrement NEXT DrawX NEXT DrawY ' The image is now printed in full and can be captured by the GET command back ' in the GetImages main program code... RETURN END SUB SUB DrawImages ' Notice that any PUT graphics statements use the _CLIP parameter and have ' an ", 8" at the end. _CLIP means the image can be placed off the screen and ' the eight at the end turns any pixel in the image with a colour value of ' 8 (Grey, same as background) into an invisible pixel for transperancy. IF DrawWhat$ = "BackGround" THEN ' Draw Background objects only... LINE (0, 0)-(319, 199), 8, BF ' Colour the background Grey ' Cycle through the pre-defined tiles using DrawTile as an index... FOR DrawTile = 0 TO 29 IF TileType(DrawTile) = 0 THEN GOSUB gsDrawFloor IF TileType(DrawTile) = 1 THEN GOSUB gsDrawWall IF TileType(DrawTile) = 2 THEN GOSUB gsDrawDoor IF TileType(DrawTile) = 15 THEN GOSUB gsDrawCrate NEXT DrawTile ' Search for green lines on the floors and place the grass image when detected GrassY = 90 FOR GrassX = 0 TO 300 STEP 20 GrassPoint = POINT(GrassX + 1, GrassY + 1) ' Top Floor IF GrassPoint = 2 THEN PUT (GrassX, GrassY), Grass(), _CLIP PSET, 8 GrassPoint = POINT(GrassX + 1, GrassY + 91) ' Bottom Floor IF GrassPoint = 2 THEN PUT (GrassX, GrassY + 90), Grass(), _CLIP PSET, 8 NEXT GrassX ELSEIF DrawWhat$ = "FixedSprites" THEN ' Draw Fixed Sprites Only... ' Nothing here now, but you could put things like switches, teleports, etc ELSEIF DrawWhat$ = "MovingSprites" THEN ' Draw Moving Sprites Only... FOR DrawTile = 0 TO 29 IF TileType(DrawTile) = 14 THEN GOSUB gsDrawDroid ' Draw the Droids NEXT DrawTile ELSE ' Draw the Decals... FOR DrawTile = 0 TO 29 IF TileType(DrawTile) = 16 THEN GOSUB gsDrawDecal ' Draw the Rocks... NEXT DrawTile END IF EXIT SUB gsDrawFloor: ' This routine basically reads the tile properties and draws a floor to match. ' The code is taken straight from another game file of mine and some parts ' are not needed here but you can play around and see what you get... LINE (TileX1(DrawTile), TileY1(DrawTile))-(TileX2(DrawTile), TileY1(DrawTile)), 0 SELECT CASE TileProperty1(DrawTile) ' What type of edges does it have? CASE 0 LINE (TileX1(DrawTile), TileY1(DrawTile))-(TileX1(DrawTile), TileY2(DrawTile)), 0 LINE (TileX1(DrawTile), TileY2(DrawTile))-(TileX2(DrawTile), TileY2(DrawTile)), 0 LINE (TileX2(DrawTile), TileY1(DrawTile))-(TileX2(DrawTile), TileY2(DrawTile)), 0 CASE 1 LINE (TileX1(DrawTile), TileY1(DrawTile))-(TileX1(DrawTile), TileY2(DrawTile) - 5), 0 LINE (TileX1(DrawTile), TileY2(DrawTile) - 5)-(TileX1(DrawTile) + 5, TileY2(DrawTile)), 0 LINE (TileX1(DrawTile) + 5, TileY2(DrawTile))-(TileX2(DrawTile) - 5, TileY2(DrawTile)), 0 LINE (TileX2(DrawTile), TileY2(DrawTile) - 5)-(TileX2(DrawTile) - 5, TileY2(DrawTile)), 0 LINE (TileX2(DrawTile), TileY1(DrawTile))-(TileX2(DrawTile), TileY2(DrawTile) - 5), 0 CASE 2 LINE (TileX1(DrawTile), TileY1(DrawTile))-(TileX1(DrawTile) + 5, TileY2(DrawTile)), 0 LINE (TileX1(DrawTile) + 5, TileY2(DrawTile))-(TileX2(DrawTile) - 5, TileY2(DrawTile)), 0 LINE (TileX2(DrawTile) - 5, TileY2(DrawTile))-(TileX2(DrawTile), TileY1(DrawTile)), 0 CASE 3 LINE (TileX1(DrawTile), TileY1(DrawTile))-(TileX1(DrawTile), TileY2(DrawTile) - 5), 0 LINE (TileX1(DrawTile), TileY2(DrawTile) - 5)-(TileX1(DrawTile) + 10, TileY2(DrawTile)), 0 LINE (TileX1(DrawTile) + 10, TileY2(DrawTile))-(TileX2(DrawTile) - 10, TileY2(DrawTile)), 0 LINE (TileX2(DrawTile), TileY2(DrawTile) - 5)-(TileX2(DrawTile) - 10, TileY2(DrawTile)), 0 LINE (TileX2(DrawTile), TileY1(DrawTile))-(TileX2(DrawTile), TileY2(DrawTile) - 5), 0 END SELECT IF TileX1(DrawTile) < 0 THEN PAINT (TileX1(DrawTile) + 20, TileY1(DrawTile) + 1), TileColour1(DrawTile), 0 IF TileX1(DrawTile) >= 0 THEN PAINT (TileX1(DrawTile) + 2, TileY1(DrawTile) + 1), TileColour1(DrawTile), 0 IF NOT TileX1(DrawTile) > TileX2(DrawTile) - 20 THEN LINE (TileX1(DrawTile) + 20, TileY1(DrawTile) + 1)-(TileX2(DrawTile) - 20, TileY1(DrawTile) + 9), TileColour2(DrawTile), BF IF TileY1(DrawTile) = 0 THEN GOTO RoofTile ' No Grass on the roof... IF TileColour1(DrawTile) = 4 THEN DrawCo1 = 112 DrawCo2 = 184 END IF IF TileColour1(DrawTile) = 6 THEN DrawCo1 = 2 DrawCo2 = 120 END IF IF TileColour1(DrawTile) > 15 AND TileColour1(DrawTile) < 25 THEN DrawCo1 = 43 DrawCo2 = 6 END IF IF TileColour1(DrawTile) > 24 AND TileColour1(DrawTile) < 32 THEN DrawCo1 = 44 DrawCo2 = 42 END IF IF TileColour1(DrawTile) = 115 THEN DrawCo1 = 42 DrawCo2 = 6 END IF IF TileColour1(DrawTile) = 53 THEN DrawCo1 = 15 DrawCo2 = 7 END IF IF TileColour1(DrawTile) = 56 THEN DrawCo1 = 48 DrawCo2 = 10 END IF LINE (TileX1(DrawTile), TileY1(DrawTile) + 1)-(TileX2(DrawTile), TileY1(DrawTile) + 1), DrawCo1 LINE (TileX1(DrawTile), TileY1(DrawTile) + 2)-(TileX2(DrawTile), TileY1(DrawTile) + 2), DrawCo2 RoofTile: ' Don't draw grass on the roof... RETURN gsDrawWall: ' This routine basically reads the tile properties and draws a Wall to match. ' The code is taken straight from another game file of mine and some parts ' are not needed here but you can play around and see what you get... LINE (TileX1(DrawTile), TileY1(DrawTile))-(TileX2(DrawTile), TileY1(DrawTile)), 0 LINE (TileX1(DrawTile) + 10, TileY1(DrawTile) + 1)-(TileX2(DrawTile) - 10, TileY2(DrawTile) - 1), TileColour1(DrawTile), BF SELECT CASE TileProperty1(DrawTile) ' What type of edges does it have? CASE 0 LINE (TileX1(DrawTile), TileY1(DrawTile))-(TileX1(DrawTile), TileY2(DrawTile)), 0 LINE (TileX1(DrawTile), TileY2(DrawTile))-(TileX2(DrawTile), TileY2(DrawTile)), 0 LINE (TileX2(DrawTile), TileY1(DrawTile))-(TileX2(DrawTile), TileY2(DrawTile)), 0 CASE 1 LINE (TileX1(DrawTile), TileY1(DrawTile))-(TileX1(DrawTile), TileY2(DrawTile) - 5), 0 LINE (TileX1(DrawTile), TileY2(DrawTile) - 5)-(TileX1(DrawTile) + 5, TileY2(DrawTile)), 0 LINE (TileX1(DrawTile) + 5, TileY2(DrawTile))-(TileX2(DrawTile) - 5, TileY2(DrawTile)), 0 LINE (TileX2(DrawTile), TileY2(DrawTile) - 5)-(TileX2(DrawTile) - 5, TileY2(DrawTile)), 0 LINE (TileX2(DrawTile), TileY1(DrawTile))-(TileX2(DrawTile), TileY2(DrawTile) - 5), 0 CASE 2 LINE (TileX1(DrawTile), TileY1(DrawTile))-(TileX1(DrawTile) + 5, TileY2(DrawTile)), 0 LINE (TileX1(DrawTile) + 5, TileY2(DrawTile))-(TileX2(DrawTile) - 5, TileY2(DrawTile)), 0 LINE (TileX2(DrawTile) - 5, TileY2(DrawTile))-(TileX2(DrawTile), TileY1(DrawTile)), 0 CASE 3 LINE (TileX1(DrawTile), TileY1(DrawTile))-(TileX1(DrawTile), TileY2(DrawTile) - 5), 0 LINE (TileX1(DrawTile), TileY2(DrawTile) - 5)-(TileX1(DrawTile) + 10, TileY2(DrawTile)), 0 LINE (TileX1(DrawTile) + 10, TileY2(DrawTile))-(TileX2(DrawTile) - 10, TileY2(DrawTile)), 0 LINE (TileX2(DrawTile), TileY2(DrawTile) - 5)-(TileX2(DrawTile) - 10, TileY2(DrawTile)), 0 LINE (TileX2(DrawTile), TileY1(DrawTile))-(TileX2(DrawTile), TileY2(DrawTile) - 5), 0 END SELECT IF TileX1(DrawTile) < 0 THEN PAINT (TileX1(DrawTile) + 20, TileY1(DrawTile) + 1), TileColour1(DrawTile), 0 IF TileX1(DrawTile) >= 0 THEN PAINT (TileX1(DrawTile) + 2, TileY1(DrawTile) + 1), TileColour1(DrawTile), 0 LINE (TileX1(DrawTile) + 1, TileY1(DrawTile))-(TileX2(DrawTile) - 1, TileY1(DrawTile)), TileColour1(DrawTile) LINE (TileX1(DrawTile) + 10, TileY1(DrawTile) + 1)-(TileX2(DrawTile) - 10, TileY2(DrawTile) - 10), TileColour2(DrawTile), BF RETURN gsDrawDoor: ' This routine draws the door either open or closed, locked or unlocked, etc LINE (TileX1(DrawTile), TileY1(DrawTile))-(TileX1(DrawTile) + 19, TileY1(DrawTile) + 19), 0, B LINE (TileX1(DrawTile) + 1, TileY1(DrawTile) - 1)-(TileX1(DrawTile) + 18, TileY1(DrawTile) + 18), TileColour1(DrawTile), BF LINE (TileX1(DrawTile) + 5, TileY1(DrawTile) - 10)-(TileX1(DrawTile) + 14, TileY1(DrawTile) + 9), TileColour2(DrawTile), BF PSET (TileX1(DrawTile), TileY1(DrawTile) + 19), 20 PSET (TileX2(DrawTile), TileY1(DrawTile) + 19), 20 ' If the door is unlocked and the man is standing under it then draw it open. IF MnY > TileY1(DrawTile) AND MnY < TileY2(DrawTile) AND MnX >= TileX1(DrawTile) - 25 AND MnX <= TileX2(DrawTile) + 26 AND TileProperty1(DrawTile) = 0 THEN PUT (TileX1(DrawTile) + 5, TileY1(DrawTile) + 20), DoorOpen(), _CLIP PSET, 8 ELSE PUT (TileX1(DrawTile) + 5, TileY1(DrawTile) + 30), DoorBottom(), _CLIP PSET, 8 PUT (TileX1(DrawTile) + 5, TileY1(DrawTile) + 20), DoorTop(), _CLIP PSET, 8 IF TileProperty1(DrawTile) = 0 THEN IF TimeCount = 1 OR TimeCount = 3 THEN LINE (TileX1(DrawTile) + 9, TileY1(DrawTile) + 39)-(TileX1(DrawTile) + 10, TileY1(DrawTile) + 50), 192, B LINE (TileX1(DrawTile) + 9, TileY1(DrawTile) + 41)-(TileX1(DrawTile) + 10, TileY1(DrawTile) + 48), 120, B LINE (TileX1(DrawTile) + 9, TileY1(DrawTile) + 43)-(TileX1(DrawTile) + 10, TileY1(DrawTile) + 46), 2, B END IF ELSE IF TimeCount = 1 OR TimeCount = 3 THEN ' Make the light flash once per second LINE (TileX1(DrawTile) + 9, TileY1(DrawTile) + 39)-(TileX1(DrawTile) + 10, TileY1(DrawTile) + 50), 112, B LINE (TileX1(DrawTile) + 9, TileY1(DrawTile) + 41)-(TileX1(DrawTile) + 10, TileY1(DrawTile) + 48), 4, B LINE (TileX1(DrawTile) + 9, TileY1(DrawTile) + 43)-(TileX1(DrawTile) + 10, TileY1(DrawTile) + 46), 40, B ELSE LINE (TileX1(DrawTile) + 9, TileY1(DrawTile) + 39)-(TileX1(DrawTile) + 10, TileY1(DrawTile) + 50), 184, B LINE (TileX1(DrawTile) + 9, TileY1(DrawTile) + 41)-(TileX1(DrawTile) + 10, TileY1(DrawTile) + 48), 112, B LINE (TileX1(DrawTile) + 9, TileY1(DrawTile) + 43)-(TileX1(DrawTile) + 10, TileY1(DrawTile) + 46), 4, B END IF END IF END IF RETURN gsDrawDroid: ' This routine draws and moves the droids, they bounce up and down based on ' the GameTimer value, whether it is negative or positive. IF TileProperty1(DrawTile) > 0 THEN IF TileProperty2(DrawTile) = 0 THEN DroidTest = POINT(TileX1(DrawTile) - 1, TileY2(DrawTile) + 1) IF DroidTest <> 0 THEN TileProperty2(DrawTile) = 1 DroidTest = POINT(TileX1(DrawTile) - 1, TileY1(DrawTile) + 1) IF DroidTest <> 8 THEN TileProperty2(DrawTile) = 1 TileX1(DrawTile) = TileX1(DrawTile) - 1 TileX2(DrawTile) = TileX2(DrawTile) - 1 IF GameTimer > 0 THEN PUT (TileX1(DrawTile), TileY1(DrawTile)), DroidLeft(), _CLIP PSET, 8 IF GameTimer < 0 THEN PUT (TileX1(DrawTile), TileY1(DrawTile) + 1), DroidLeft(), _CLIP PSET, 8 ELSE DroidTest = POINT(TileX2(DrawTile) + 1, TileY2(DrawTile) + 1) IF DroidTest <> 0 THEN TileProperty2(DrawTile) = 0 DroidTest = POINT(TileX2(DrawTile) + 1, TileY1(DrawTile) + 1) IF DroidTest <> 8 THEN TileProperty2(DrawTile) = 0 TileX1(DrawTile) = TileX1(DrawTile) + 1 TileX2(DrawTile) = TileX2(DrawTile) + 1 IF GameTimer > 0 THEN PUT (TileX1(DrawTile), TileY1(DrawTile)), DroidRight(), _CLIP PSET, 8 IF GameTimer < 0 THEN PUT (TileX1(DrawTile), TileY1(DrawTile) + 1), DroidRight(), _CLIP PSET, 8 END IF END IF RETURN gsDrawCrate: ' Put a wooden crate on the screen somewhere... IF TileProperty1(DrawTile) > 0 THEN IF TileProperty2(DrawTile) = 0 THEN PUT (TileX1(DrawTile), TileY1(DrawTile) + 1), CrateWood(), _CLIP PSET, 8 END IF RETURN gsDrawDecal: ' Draw some rocks... SELECT CASE TileProperty1(DrawTile) CASE 2 IF TileProperty2(DrawTile) = 0 THEN PUT (TileX1(DrawTile), TileY1(DrawTile)), RockDecalBottom(), _CLIP PSET, 8 IF TileProperty2(DrawTile) = 1 THEN PUT (TileX1(DrawTile), TileY1(DrawTile)), RockDecalBottom1(), _CLIP PSET, 8 IF TileProperty2(DrawTile) = 2 THEN PUT (TileX1(DrawTile), TileY1(DrawTile)), RockDecalTop(), _CLIP PSET, 8 IF TileProperty2(DrawTile) = 3 THEN PUT (TileX1(DrawTile), TileY1(DrawTile)), RockDecalTop1(), _CLIP PSET, 8 END SELECT RETURN END SUB SUB DrawMan ' This SUB routine draws the man in his many varying states, you can follow ' it down line for line to work out which frame gets drawn and why... IF MnHealth >= 315 THEN SELECT CASE MnHurtOn CASE IS > 28, IS < -28 IF MnSide = 0 THEN PUT (MnX - 20, MnY), ManLDie(), _CLIP PSET, 8 IF MnSide = 1 THEN PUT (MnX - 20, MnY), ManRDie(), _CLIP PSET, 8 CASE IS > 26, IS < -26 IF MnSide = 0 THEN PUT (MnX - 20, MnY), ManLDie1(), _CLIP PSET, 8 IF MnSide = 1 THEN PUT (MnX - 20, MnY), ManRDie1(), _CLIP PSET, 8 CASE IS > 24, IS < -24 IF MnSide = 0 THEN PUT (MnX - 20 + 20, MnY + 7), ManLDie2(), _CLIP PSET, 8 IF MnSide = 1 THEN PUT (MnX - 20 - 20, MnY + 7), ManRDie2(), _CLIP PSET, 8 CASE IS > 20, IS < -20, IS > 10 IF MnSide = 0 THEN PUT (MnX - 20 + 20, MnY + 7), ManLDie3(), _CLIP PSET, 8 IF MnSide = 1 THEN PUT (MnX - 20 - 20, MnY + 7), ManRDie3(), _CLIP PSET, 8 END SELECT EXIT SUB END IF IF MnWeapon = 0 THEN SELECT CASE MnFireTimer CASE 8, 16 IF MnCrouchOn = 0 THEN IF MnSide = 0 THEN PUT (MnX - 20 + 2, MnY), ManLfi8(), _CLIP PSET, 8 IF MnSide = 1 THEN PUT (MnX - 20 - 2, MnY), ManRfi8(), _CLIP PSET, 8 ELSE IF MnSide = 0 THEN PUT (MnX - 20 + 2, MnY), ManLCfi8(), _CLIP PSET, 8 IF MnSide = 1 THEN PUT (MnX - 20 - 2, MnY), ManRCfi8(), _CLIP PSET, 8 END IF CASE 7, 15 IF MnCrouchOn = 0 THEN IF MnSide = 0 THEN PUT (MnX - 20 + 2, MnY), ManLfi7(), _CLIP PSET, 8 IF MnSide = 1 THEN PUT (MnX - 20 - 2, MnY), ManRfi7(), _CLIP PSET, 8 ELSE IF MnSide = 0 THEN PUT (MnX - 20 + 2, MnY), ManLCfi7(), _CLIP PSET, 8 IF MnSide = 1 THEN PUT (MnX - 20 - 2, MnY), ManRCfi7(), _CLIP PSET, 8 END IF CASE 6, 14 IF MnCrouchOn = 0 THEN IF MnSide = 0 THEN PUT (MnX - 20 + 1, MnY), ManLfi6(), _CLIP PSET, 8 IF MnSide = 1 THEN PUT (MnX - 20 - 1, MnY), ManRfi6(), _CLIP PSET, 8 ELSE IF MnSide = 0 THEN PUT (MnX - 20 + 1, MnY), ManLCfi6(), _CLIP PSET, 8 IF MnSide = 1 THEN PUT (MnX - 20 - 1, MnY), ManRCfi6(), _CLIP PSET, 8 END IF CASE 5, 13 IF MnCrouchOn = 0 THEN IF MnSide = 0 THEN PUT (MnX - 20 + 1, MnY), ManLfi5(), _CLIP PSET, 8 IF MnSide = 1 THEN PUT (MnX - 20 - 1, MnY), ManRfi5(), _CLIP PSET, 8 ELSE IF MnSide = 0 THEN PUT (MnX - 20 + 1, MnY), ManLCfi5(), _CLIP PSET, 8 IF MnSide = 1 THEN PUT (MnX - 20 - 1, MnY), ManRCfi5(), _CLIP PSET, 8 END IF CASE 4, 3, 2, 1, 0, 12, 11, 10, 9 IF MnCrouchOn = 0 THEN IF (MnWalkOn = 0 OR MnWalkOn = -1 OR MnWalkOn = -4 OR MnWalkOn = 1 OR MnWalkOn = 4) AND MnJumpOn = 0 THEN IF MnSide = 0 THEN PUT (MnX - 20, MnY), ManL(), _CLIP PSET, 8 IF MnSide = 1 THEN PUT (MnX - 20, MnY), ManR(), _CLIP PSET, 8 END IF IF MnWalkOn = -2 OR MnWalkOn = -3 OR MnWalkOn = 2 OR MnWalkOn = 3 OR MnJumpOn = 1 THEN IF MnSide = 0 THEN PUT (MnX - 20, MnY), ManLStep(), _CLIP PSET, 8 IF MnSide = 1 THEN PUT (MnX - 20, MnY), ManRStep(), _CLIP PSET, 8 END IF ELSE IF MnSide = 0 THEN PUT (MnX - 20, MnY), ManLCrouch(), _CLIP PSET, 8 IF MnSide = 1 THEN PUT (MnX - 20, MnY), ManRCrouch(), _CLIP PSET, 8 END IF END SELECT ELSE SELECT CASE MnFireTimer CASE 8, 16 IF MnCrouchOn = 0 THEN IF MnSide = 0 THEN PUT (MnX - 20 + 2, MnY), ManLPRfi8(), _CLIP PSET, 8 IF MnSide = 1 THEN PUT (MnX - 20 - 2, MnY), ManRPRfi8(), _CLIP PSET, 8 ELSE IF MnSide = 0 THEN PUT (MnX - 20 + 2, MnY), ManLPRCfi8(), _CLIP PSET, 8 IF MnSide = 1 THEN PUT (MnX - 20 - 2, MnY), ManRPRCfi8(), _CLIP PSET, 8 END IF CASE 7, 15 IF MnCrouchOn = 0 THEN IF MnSide = 0 THEN PUT (MnX - 20 + 2, MnY), ManLPRfi7(), _CLIP PSET, 8 IF MnSide = 1 THEN PUT (MnX - 20 - 2, MnY), ManRPRfi7(), _CLIP PSET, 8 ELSE IF MnSide = 0 THEN PUT (MnX - 20 + 2, MnY), ManLPRCfi7(), _CLIP PSET, 8 IF MnSide = 1 THEN PUT (MnX - 20 - 2, MnY), ManRPRCfi7(), _CLIP PSET, 8 END IF CASE 6, 14 IF MnCrouchOn = 0 THEN IF MnSide = 0 THEN PUT (MnX - 20 + 1, MnY), ManLPRfi6(), _CLIP PSET, 8 IF MnSide = 1 THEN PUT (MnX - 20 - 1, MnY), ManRPRfi6(), _CLIP PSET, 8 ELSE IF MnSide = 0 THEN PUT (MnX - 20 + 1, MnY), ManLPRCfi6(), _CLIP PSET, 8 IF MnSide = 1 THEN PUT (MnX - 20 - 1, MnY), ManRPRCfi6(), _CLIP PSET, 8 END IF CASE 5, 13 IF MnCrouchOn = 0 THEN IF MnSide = 0 THEN PUT (MnX - 20 + 1, MnY), ManLPRfi5(), _CLIP PSET, 8 IF MnSide = 1 THEN PUT (MnX - 20 - 1, MnY), ManRPRfi5(), _CLIP PSET, 8 ELSE IF MnSide = 0 THEN PUT (MnX - 20 + 1, MnY), ManLPRCfi5(), _CLIP PSET, 8 IF MnSide = 1 THEN PUT (MnX - 20 - 1, MnY), ManRPRCfi5(), _CLIP PSET, 8 END IF CASE 4, 3, 2, 1, 0, 12, 11, 10, 9 IF MnCrouchOn = 0 THEN IF (MnWalkOn = 0 OR MnWalkOn = -1 OR MnWalkOn = -4 OR MnWalkOn = 1 OR MnWalkOn = 4) AND MnJumpOn = 0 THEN IF MnSide = 0 THEN PUT (MnX - 20, MnY), ManLPR(), _CLIP PSET, 8 IF MnSide = 1 THEN PUT (MnX - 20, MnY), ManRPR(), _CLIP PSET, 8 END IF IF MnWalkOn = -2 OR MnWalkOn = -3 OR MnWalkOn = 2 OR MnWalkOn = 3 OR MnJumpOn = 1 THEN IF MnSide = 0 THEN PUT (MnX - 20, MnY), ManLPRStep(), _CLIP PSET, 8 IF MnSide = 1 THEN PUT (MnX - 20, MnY), ManRPRStep(), _CLIP PSET, 8 END IF ELSE IF MnSide = 0 THEN PUT (MnX - 20, MnY), ManLPRCrouch(), _CLIP PSET, 8 IF MnSide = 1 THEN PUT (MnX - 20, MnY), ManRPRCrouch(), _CLIP PSET, 8 END IF END SELECT END IF END SUB SUB Hud ' Timer ' Draw the Grey metal box around the timer... LINE (140, 0)-(179, 15), 254, BF LINE (130, 0)-(139, 19), 254 LINE (189, 0)-(180, 19), 254 LINE (140, 19)-(179, 19), 254 PAINT (135, 0), 25, 254 LINE (131, 0)-(139, 17), 27 LINE (188, 0)-(180, 17), 23 LINE (140, 18)-(179, 18), 23 LINE (139, 0)-(139, 15), 23, BF LINE (180, 0)-(180, 15), 27, BF LINE (140, 16)-(179, 16), 27 ' If you've been playing for more than 5 minutes the clock turns red. IF Mins < 5 THEN COLOR 120 IF Mins > 4 THEN COLOR 112 ' Convert the Mins and Secs values into strings and trim them. LOCATE 1, 19: PRINT LTRIM$(STR$(Mins)); " 0" IF Secs < 10 THEN LOCATE 1, 22: PRINT LTRIM$(STR$(Secs)) IF Secs > 9 THEN LOCATE 1, 21: PRINT LTRIM$(STR$(Secs)) ' Make the clock ":" ficker like an alarm clock. IF (TimeCount = 1 OR TimeCount = 3) AND Mins < 5 THEN COLOR 2 IF (TimeCount = 1 OR TimeCount = 3) AND Mins > 4 THEN COLOR 4 LOCATE 1, 20: PRINT ":" ' A finishing touch to the metal exterior. PSET (140, 15), 21 PSET (179, 15), 21 ' Ammuntion ' Reload your shells if you run out. IF MnAmmo < 9 AND TimeCount = 0 AND GameTimer = 1 THEN MnAmmo = MnAmmo + 3 ' Draw the Empty Shell Casings... 90 is Full Ammo. FOR empty = 3 TO 90 STEP 3 LINE (empty, 193)-(empty, 196), 18 LINE (empty + 1, 193)-(empty + 1, 196), 20 NEXT empty ' Draw the Shells based on your MnAmmo Status... IF MnAmmo = 0 THEN ' No Ammo? Draw Nothing! ELSE ' Draw each shell 3 pixels apart until MnAmmo is reached. FOR HUDamo = 3 TO MnAmmo STEP 3 PSET (HUDamo, 193), 44 PSET (HUDamo + 1, 193), 43 LINE (HUDamo, 194)-(HUDamo, 196), 40 LINE (HUDamo + 1, 194)-(HUDamo + 1, 196), 4 NEXT HUDamo END IF ' Hearts ' Draw the hearts 10 pixels apart working backwards from 315 FOR HudHeart = 315 TO MnHealth STEP -10 ' Make the hearts flash once per second... IF TimeCount = 1 OR TimeCount = 3 THEN PUT (HudHeart, 181), SmallHeart(), _CLIP PSET, 8 ELSE PUT (HudHeart, 181), SmallHeart1(), _CLIP PSET, 8 END IF ' Draw the Half Hearts if any... IF MnHealth = HudHeart - 5 THEN IF TimeCount = 1 OR TimeCount = 3 THEN PUT (HudHeart - 10, 181), HalfHeart(), _CLIP PSET, 8 ELSE PUT (HudHeart - 10, 181), HalfHeart1(), _CLIP PSET, 8 END IF END IF NEXT HudHeart END SUB