Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /** Bouncing ball game by Donkeysoft MMXII
- * Written for the Sinclair ZX81, requires
- * 16K of RAM to run. Release candidate v1.0000000.
- * See www.z88dk.org for information about the
- * zcc compiler. */
- #include <zx81.h>
- #include <stdio.h>
- #include <input.h>
- // Definitions for ROM calls:
- #define POKEY 0x4005
- #define PRINTAT 0x08f5
- // The following definitions are used in the print(int a);
- // sub-rountine or function or method. Basically, there's
- // a zxcursorat(x,y) routine included in the ZX81 library
- // but unfortunately this didn't work and crashed my code,
- // so I had to make a couple of small assembly routines to
- // set the cursor position and also print a character to
- // the canvas. #defining stuff seemed the safest way to
- // send the necessary character codes to the print()
- // function.
- #define CLEAR 0x00
- #define BALL 0x34
- #define BAT 0x80
- #define BORDER 0x08
- #define GAMEROW 0x0a
- // Defines start of numbers:
- #define ZERO 0x1c
- // 1 will be ZERO+1 etc...
- // ZX81 chars (BALL is defined):
- #define CHARA 0x26
- #define _B 0x27
- #define _C 0x28
- #define _D 0x29
- #define _E 0x2a
- #define _F 0x2b
- #define _G 0x2c
- #define _H 0x2d
- #define _I 0x2e
- #define _J 0x2f
- #define _K 0x30
- #define _L 0x31
- #define _M 0x32
- #define _N 0x33
- // O is BALL above ;-)
- //#define _O 0x34
- #define _P 0x35
- // Q will be _P+1
- #define _R 0x37
- #define _S 0x38
- #define _T 0x39
- #define _U 0x3a
- // V will be U+1
- // W will be U+2
- // X will be U+3
- // Y will be U+4
- // Z will be U+5
- // Function prototypes:
- static void main(void);
- static void title(void);
- static void waitforkey(void);
- static void clearcanvas(void);
- static void play(void);
- static void printlives(void);
- static void increaselevel(void);
- static void levelup(void);
- static void pausegame(void);
- static void restart(void);
- static void printat(short x, short y);
- static void print(int a);
- // x0 is current screen position, x1 is last screen position
- // and x2 is direction
- static short x0=0;
- static short x1=0;
- static short x2=1;
- // Ditto for y0, y1 and y2
- static short y0=0;
- static short y1=0;
- static short y2=1;
- // This is the boundary for our X axis:
- // Unused game feature
- // static char minx=1;
- // static char maxx=30;
- // I was thinking of the walls closing in from the sides too
- // but decided against it.
- // This awards a bonus point on the internal
- // scoring if you make the ball go the other way:
- static char skills=0;
- // If you get it right eight times, you get an extra life,
- // so we need to count the number of times:
- static char skillcount=0;
- // This is used to define the top of the play area:
- static char top=1;
- // Player position (and at line 20):
- static char px=14;
- // Re-usable variables:
- static short a=0;
- static short i=0;
- // Number of lives:
- static char lives=8;
- // Score:
- // Score default is set to one because of the way the extra
- // lives are rewarded. I could have added && (score!=0) to
- // the condition I suppose, but as the scoring system is not
- // seen by the player, it doesn't matter either way. Plus
- // adding an extra condition might translate into more of the
- // CPUs time being eaten away, but probably only by a few cycles...
- // This might matter on bigger projects.
- static short score=1;
- // Check if missed or not:
- static char hit=1;
- static char once=0;
- // Difficulty level, higher is easier:
- static char dif=6;
- // This is the actual level:
- static char level=0;
- // 1=dead:
- static char dead=0;
- // Used to pause game, 1=pause
- static char pause=0;
- // For key inputs:
- static char key=' ';
- // For game over message, ie, the players rating when lives
- // are spent. Basically, each rating is 5 characters so that
- // I can say a=level*5; for(i=0;i<5;i=i+1) { print(fail[i+a]; }:
- static int fail[]=
- {
- _F, CHARA, _I, _L, BORDER,
- _L, BALL, _S, _E, _R,
- _B, BALL, _U+5, BALL, BORDER,
- _D, BALL, _P, _E, _U+4,
- BORDER, _B, CHARA, _D, BORDER,
- BALL, _K, CHARA, _U+4, BORDER,
- _S, CHARA, _F, _E, BORDER,
- _G, BALL, BALL, _D, BORDER,
- _S, _U+2, _E, _E, _T,
- BORDER, _H, BALL, _T, BORDER,
- _G, _R, _E, CHARA, _T,
- _S, _U, _P, _E, _R,
- BORDER, CHARA, _C, _E, BORDER,
- _C, BALL, BALL, _L, BORDER,
- _S, _T, CHARA, _R, BORDER,
- _C, _R, CHARA, _U+5, _U+4,
- _E, _P, _I, _C, BORDER,
- _K, BALL, BALL, _L, BORDER
- };
- // Level names - each four characters long for the above reason:
- static int levels[]=
- {
- _U+5, _U+5, _U+5, _U+5,
- _D, _U, _L, _L,
- _P, _U, _N, _U+4,
- _B, BALL, _R, _E,
- _S, _L, BALL, _U+2,
- _F, _L, CHARA, _T,
- _S, BALL, _M, CHARA,
- _P, _R, _E, _P,
- _S, CHARA, _F, _E,
- _G, BALL, BALL, _D,
- _H, _E, _L, _L,
- BALL, CLEAR, _M, _U,
- _M, CLEAR, _H, _E,
- _L, _L, BALL, CLEAR,
- _R, _U, _B, _U+4,
- CLEAR, _M, CHARA, _E,
- _U+5, _U+3, ZERO+8, ZERO,
- _U+5, _U+3, ZERO+8, ZERO+1
- };
- // Game over message, 13 used for newline and 255 used
- // as terminator:
- static int gameover[]=
- {
- _P, _R, _E, _S, _S, CLEAR, _T, CLEAR, _F, BALL, _R, CLEAR,
- _T, _I, _T, _L, _E, CLEAR, BALL, _R, CLEAR, _T, _H, _E,
- CLEAR, CHARA, _N, _U+4, 13, CLEAR, _K, _E, _U+4,
- CLEAR, _T, BALL, CLEAR, _R, _E, _P, _L, CHARA, _U+4, 255
- };
- // This condition is set to true when a new level is entered
- // so that the program knows to update the display:
- static char newlevel=0;
- // This will print BALLS at the appropriate place on the screen:
- static int balls[]=
- {
- _B, CHARA, _L, _L, _S, 255
- };
- // Title screen message:
- static uchar titletext[]="\n\n --= DONKEYSOFT MMXII =--\n\n PRESENTS\n\n BOUNCE\n\n 5 : MOVE BAT LEFT AND\n\n 8 : MOVE BAT RIGHT\n\n P : PAUSE GAME\n\n R : UNPAUSE GAME\n\n PRESS THE ANY KEY TO PLAYZ";
- void main(void)
- {
- if(!dead)
- {
- // This bit of code sets the RAM TOP to max
- // and means it's a bit more stable on machines
- // with more than 16K - or at least on the emulator:
- #asm
- PUSH HL
- LD HL,(POKEY)
- LD A,$ff
- LD (HL),A
- POP HL
- #endasm
- // Ensures that the ZX81 is in 'slow' mode:
- zx_slow();
- // Prints a nice message welcome message:
- printk("FAIRLY STABLE VERSION OF BOUNCE THIS IS ABOUT AS GOOD AS I'M ABLE TO GET THINGS AT THE MOMENTAS I'M USING Z88DK. MOVE THE BATLEFT AND RIGHT TO KEEP IT IN PLAY. PRESS THE ANY KEY --->>\n\n\nPLEASE LEAVE FEEDBACK ABOUT THISGAME\n\n\n\n(C) JANUARY 2012 DONKEYSOFT\n\nVERSION V1.0000000\n--------------------------------");
- // Delay in milliseconds:
- in_Wait(128);
- // Sets cursor position:
- printat(0,0);
- // Waits for keyboard input:
- in_WaitForKey();
- // Stores key pressed in the variable key:
- key=in_Inkey();
- // Clears the screen:
- }
- // Clears the screen and calls the title(); function, basically
- // a title screen:
- printk("%c ",12);
- title();
- }
- // This is the main bit of code that does the title screen:
- void title(void)
- {
- // Sets temporary variables:
- i=0; x0=0; y0=0;
- // Condition prints the title screen character by character:
- while(titletext[i]!='Z')
- {
- // My printat(x,y); function can be set to a different
- // position from the stdio printk(); function - so my
- // print function follows the printat(), whereas for
- // the version of z88dk I was using, the zxsetcursor(x,y);
- // didn't work at all:
- print(CLEAR);
- // Takes a copy of the last cursor position:
- x1=x0; y1=y0;
- // prints the character from the char array:
- printk("%c",titletext[i]);
- // prints char at current position:
- printat(x0,y0);
- // prints an 'inversed L' character, which will be familair
- // to ZX81 users:
- print(177);
- // If there's a new line then the x0 and y0 needs updating
- // to trace where the cursor is at on the printk(); function:
- if(titletext[i]=='\n')
- {
- print(CLEAR);
- y0=y0+1; x0=0;
- }
- // increase our index:
- i=i+1;
- // increase on our x axis for our char:
- x0=x0+1;
- // updates the char position for the print(); function:
- printat(x1,y1);
- }
- // Finally, print the inversed L when finished:
- print(177);
- // reset the cursor position for the print(); function:
- printat(0,0);
- // The following code will print a border around the screen.
- // It would be faster to 'POKE' the byte directly to the DFile
- // but this isn't stable in z88dk. This is a little slow but
- // it works without crashing:
- for(i=0;i<32;i=i+1)
- {
- print(BORDER);
- }
- for(i=1;i<22;i=i+1)
- {
- printat(0,i);
- print(BORDER);
- printat(31,i);
- print(BORDER);
- }
- printat(0,21);
- for(i=0;i<=31;i=i+1)
- {
- print(BORDER);
- }
- // This now sets up the canvas for play, printing BALLS as the
- // lives indicator and the starting level:
- printat(3,21);
- i=0;
- while(balls[i]!=255)
- {
- print(balls[i]);
- i=i+1;
- }
- printat(25,21);
- for(i=0;i<4;i=i+1)
- {
- print(levels[i]);
- }
- // Again, print's the inversed L but at the end of the title
- // text - not sure if this is entirely necessary, I think
- // it might be for presentation purposes... Comment out the
- // next two lines and see:
- printat(x1,y1);
- print(177);
- // The waitforkey(); function resets the game values and waits
- // for a key to start the game:
- waitforkey();
- }
- // As above:
- void waitforkey()
- {
- // Resets everything if the title screen is
- // called again, ie, a game has already been
- // played:
- if(dead)
- {
- dead=0;
- top=1;
- dif=6;
- lives=8;
- score=1;
- px=14;
- hit=0;
- once=0;
- level=0;
- skills=0;
- skillcount=0;
- }
- in_WaitForKey();
- // This will check which any key is pressed and set some
- // values before starting the game:
- key=in_Inkey();
- if(key>1 && key<63)
- {
- x0=4; y0=2;
- }
- else
- if(key>64 && key<127)
- {
- x0=5; y0=3;
- }
- else
- if(key>128 && key<191)
- {
- x0=6; y0=4;
- }
- else
- if(key>192 && key<255)
- {
- x0=7; y0=5;
- }
- else
- {
- // This is the secret 'trainer' mode, set if the
- // player has pressed the correct any key:
- x0=1; y0=6; lives=12; top=1;
- levelup();
- top=top+1;
- dif=dif+1;
- }
- // Simple function to clear the canvas:
- clearcanvas();
- // Prints the bottom row, also where the bat resides:
- printat(1,20);
- for(i=0;i<30;i=i+1)
- {
- print(GAMEROW);
- }
- // Level up adds a new row of bricks:
- levelup();
- // increase the top of screen marker:
- top=top+1;
- // printat(9,21);
- // Calls the main game routine:
- play();
- }
- void clearcanvas(void)
- {
- // Clears the canvas and prints the default number of
- // lives:
- for(i=1;i<=19;i=i+1)
- {
- for(a=0;a<30;a=a+1)
- {
- printat(a+1,i);
- print(CLEAR);
- }
- }
- if(lives>0)
- {
- printlives();
- }
- }
- void play(void)
- {
- // This is simple, do the code while not dead :-)
- while (!dead)
- {
- // Gets keyboard input and stores it as a variable in
- // the key var:
- key=in_Inkey();
- // Current position of ball:
- printat(x0,y0);
- print(BALL);
- // current position of bat:
- printat(px,20);
- // Prints the bat, length is determined by the dif variable:
- for(i=0;i<dif;i=i+1)
- {
- print(BAT);
- }
- // Checks for new level condition to update the level code:
- if(newlevel)
- {
- printat(25,21);
- a=level*4;
- for(i=0;i<4;i=i+1)
- {
- print(levels[a+i]);
- }
- // Resets newlevel condition:
- newlevel=0;
- }
- // Moves bat right on the X axis:
- if(key=='5' && px>1)
- {
- printat(px, 20);
- px=px-1;
- printat(px-1, 20);
- // This clears the bat position without using the space
- // character, which is 'flickery':
- if(px==1)
- {
- print(BORDER);
- }
- else
- if(px>1)
- {
- print(GAMEROW);
- }
- // Prints current size of bat followed by the character
- // used on the bottom row:
- for(i=0;i<dif;i=i+1)
- {
- print(BAT);
- }
- print(GAMEROW);
- }
- // Moves right on the X axis using the same logic as above
- // but obviously for right movements:
- if(key=='8' && px<31-dif)
- {
- printat(px, 20);
- px=px+1;
- printat(px-1, 20);
- print(GAMEROW);
- for(i=0;i<dif;i=i+1)
- {
- print(BAT);
- }
- if(px+dif==31)
- {
- print(BORDER);
- }
- else
- if(px+dif<31)
- {
- print(GAMEROW);
- }
- }
- // Secret key, slows down play *but* the player can't move
- // whilst zero is depressed:
- if(key=='0')
- {
- in_Wait(64);
- }
- // Checks for the P key, ie, pause:
- if(key==80)
- {
- printat(x0,y0);
- print(BALL);
- pause=1;
- pausegame();
- }
- // Prints position of ball again, I think doing this twice (?)
- // makes the animation a bit better:
- printat(x0, y0);
- print(BALL);
- // Copies old locations and moves the ball to a new position:
- x1=x0; y1=y0; x0=x0+x2; y0=y0+y2;
- //Checks for the boundaries of the play area for the ball and
- // ensures that it 'bounces' if it hits the boundaries... This
- // happens even if the player misses the ball so that the play
- // is constant until all lives have been lost:
- if (x0<1)
- {
- x0=1; x2=1;
- }
- if (x0>30)
- {
- x0=30; x2=-1;
- }
- if (y0<top)
- {
- y0=top; y2=1; once=0; hit=0;
- }
- if (y0>19)
- {
- y0=19; y2=-1;
- }
- if (y0==19 && (x0<px || x0>px+dif))
- {
- // This simply checks if the player has legally hit the ball
- // first due to the way the ball bounces around the play
- // area:
- if(once)
- {
- hit=1;
- }
- else
- {
- hit=0;
- }
- // So, if the player has not hit the ball...
- if(!hit)
- {
- lives=lives-1;
- hit=1; once=1;
- // Prints the number of lives remaining:
- printlives();
- // Prints the border char to 'clear' the last ball
- // so that the player can keep track of the number
- // of lives remaining:
- print(BORDER);
- }
- // If lives are spent...
- if(lives<=0)
- {
- // Once dead is 'true' then all code within this while
- // condition stops:
- dead=1;
- }
- }
- // Okay, so it might be that the player has hit the ball if the
- // conditions are true:
- if (y0==19 && (x0>=px && x0<=px+dif))
- {
- // Says that the ball has been legally hit:
- once=0; hit=1;
- // Increases internal scoring system:
- score=score+1;
- // Checks score against conditions for increasing the
- // level and also checks for extra lives awarded:
- increaselevel();
- // This will allow the ball to be hit in the opposite
- // direction it's travelling on the X axis as long as
- // the conditions are true:
- if((x0==px || x0==px+1) && (x0>=3 && key=='5'))
- {
- // Congratulations, a bonus point is awarded and so
- // is a skill point (eventually).
- x2=-1; score=score+1; skills=1;
- // Checks score again:
- increaselevel();
- }
- // Same logic as above but for forcing the ball right:
- if((x0==px+dif || x0==px+dif-1) && (x0<=27 && key=='8'))
- {
- x2=1; score=score+1; skills=1;
- increaselevel();
- }
- }
- // Rewards an extra life when score is 100 or a multiple thereof:
- if(score%100==0 && lives<13)
- {
- lives=lives+1;
- printlives();
- }
- // Checks skills boolean:
- if(skills)
- {
- skills=0;
- skillcount=skillcount+1;
- }
- // If the player has eight skill points, and they have
- // 12 lives or less, an extra life is rewarded:
- if(skillcount==8 && lives<13)
- {
- lives=lives+1;
- skillcount=0;
- printlives();
- }
- // Sets cursor to old position of ball:
- printat(x1, y1);
- // Works out length of delay according to the level the player is on:
- a=(2*dif)-(y1-level)+22;
- if(y1<16 && y2==-1)
- {
- // This slows the ball slightly as it travels
- // up the screen, relitive to its' position:
- a=a+24;
- for(i=0;i<y1;i=i+1)
- {
- a=a-1;
- }
- }
- // General delay for game worked out above... in theory
- // the ball falls faster than it rises:
- for(i=0;i<=a;i=i+1)
- {
- #asm
- nop
- #endasm
- }
- // Clears ball:
- print(CLEAR);
- }
- // Oops, all lives are lost!
- // Print's final position of ball before restarting:
- printat(x0,y0);
- print(BALL);
- printat(9,21);
- print(BORDER);
- restart();
- in_WaitForKey();
- key=in_Inkey();
- if(key=='T')
- {
- main();
- }
- level=0;
- printat(25,21);
- for(i=0;i<4;i=i+1)
- {
- print(levels[level+i]);
- }
- waitforkey();
- }
- void increaselevel()
- {
- // Sets new level, slightly increasing the difficulty by speeding
- // things up very slightly:
- if(score==10 || score==20 || score==220 || score==250 || score==275)
- {
- level=level+1;
- score=score+1;
- newlevel=1;
- }
- // Increases difficulty level by decreasing the size of bat:
- if(score==45 || score==112 || score==130 || score==175)
- {
- dif=dif-1;
- score=score+1;
- level=level+1;
- newlevel=1;
- printat(px, 20);
- for(i=0;i<dif;i=i+1)
- {
- print(BAT);
- }
- if(px+dif==31)
- {
- print(BORDER);
- }
- else
- if(px+dif<31)
- {
- print(GAMEROW);
- }
- }
- // This increases difficulty based on score:
- if(score==30 || score==65 || score==80 || score==100 || score==125 || score==150 || score==200 || score==300)
- {
- // Makes things a little more claustrophobic:
- levelup();
- top=top+1;
- score=score+1;
- level=level+1;
- newlevel=1;
- }
- }
- // This function or method or sub-routine adds a new row to the game
- // to reduce the size of the play area:
- void levelup()
- {
- printat(1,top);
- for(i=0;i<31;i=i+1)
- {
- print(BORDER);
- }
- return;
- }
- // Prints number of lives remaining:
- void printlives()
- {
- printat(9,21);
- for(i=1;i<=lives;i=i+1)
- {
- print(BALL);
- }
- }
- // Pause routine:
- void pausegame()
- {
- while(pause)
- {
- key=in_Inkey();
- if(key==82)
- {
- pause=0;
- }
- }
- }
- // This prints the player's rating before restarting:
- void restart()
- {
- a=level*5;
- for(i=0;i<5;i=i+1)
- {
- printat(14+i,1);
- print(fail[a+i]);
- in_Wait(40);
- }
- // Prints a message asking the player to press T for title screen
- // or press the any key to play again:
- printat(2,10);
- a=0;
- while(gameover[a]!=13)
- {
- print(gameover[a]);
- a=a+1;
- }
- a=a+1;
- printat(8,11);
- while(gameover[a]!=255)
- {
- print(gameover[a]);
- a=a+1;
- }
- // This checks that the player still isn't holding down the key from
- // playing the game, so that the game doesn't restart too quickly:
- while(key!=0)
- {
- key=in_Inkey();
- }
- printat(1,1);
- }
- // A little bit of assembly is a good thing, this sets the X and Y
- // position of the cursor for the print(); routine:
- void printat(short x, short y)
- {
- #asm
- PUSH BC
- PUSH HL
- LD HL,6
- ADD HL,SP
- LD B,(HL)
- INC HL
- INC HL
- LD C,(HL)
- CALL PRINTAT
- POP HL
- POP BC
- RET
- #endasm
- }
- // This prints out our char:
- void print(int a)
- {
- #asm
- PUSH AF
- PUSH HL
- LD HL,6
- ADD HL,SP
- LD A,(HL)
- RST $10
- POP HL
- POP AF
- RET
- #endasm
- }
- // Compile with:
- // zcc +zx81 -startup=2 -create-app -DTEXT -o BOUNCE BounceX.c
- // (C) 2012 Donkeysoft
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement