//Sid Spanners (v1.0) // //Digital Prawn, 26th December 2010 // //Public Domain // //Data structures //All sprites 16x16 Pixels //All levels consist 32x16 tiles //All tiles are "character code" based 8x8 cells //Sprite engine set to only manage uppermost 32x16 character area of the screen // // Memory map // // 23755 ZX BASIC loader // 24576 3K Heap (in contended RAM) // 27648 Start of compiled object code // 52480 ($CD00) 257-byte ISR table // 52942 ($CECE) Three-byte jump instruction to ISR // 52945 Lowest possible area used by stack (allocated size=311 bytes) // 53246 Highest word on the stack // 53248 Sprite library rotation tables/scratch area //Must compile with -zorg=27648 //Compiling this game //Within the below comment is the Makefile that was used to build the game //Either copy and paste this into a file called "Makefile" and build with "make" //Or just compile with zcc directly using all options shown below //zcc v2.57 was originally used to build the game /* sidspan.tap: sidspan.c zcc +zx -osidspan.bin -lndos -lmalloc -lim2 -lsp1 sidspan.c -zorg=27648 -create-app */ #include #include #include #include #include #include #include #include #include #include #include //Pre-processor directives, definitions and macros #define SGN(x) ((x)<0?-1:((x)?1:0)) #define ABS(x) ((x)<0?-(x):(x)) #define WAIT_KEY while(!in_Inkey()) #pragma output STACKPTR=53248 // Set stack at high location in uncontended RAM #define MAX_SPRITES 5 #define NUM_LEVELS 8 #define START_LEVEL 1 //for debugging #define LEVEL_SIZE 512 #define HEAP_ADDR 24576 //8K heap in low RAM (slow) #define HEAP_SIZE 3072 //Typedefs typedef unsigned char byte; typedef unsigned int word; //My sprite data structure struct sprentry { struct sp1_ss *s; char dx; char dy; byte frame; //pixel position of sprite on screen (used for collision detection and bounding) byte px; byte py; //start positions (player and guardians) byte startcol; byte startrow; //end positions (guardians only) byte endcol; byte endrow; //1=travelling towards end //255 (-1)=travelling back to start byte direction; }; //Globals long heap; // malloc's heap pointer struct in_UDK keys; //structure for keyboard input void *getjoy; //function pointer for input routine - has to be declared as "void *" //because function pointer declarations not supported in Z88DK struct sprentry sprtbl[MAX_SPRITES]; //Sprite table char *current_map; //pointer to map data of current level byte airborne; //set to 1 if the player character is in the air byte facing_left; //set to 1 if facing left else 0 for facing right byte frame_tick,jump_tick,guardian_flip,guardian_tick; byte sprite_colour[MAX_SPRITES]; byte colour; //needed as a global because cannot be passed as parameter into add_sprite_colour() hook byte global_n; //generic global byte used instead of a function parameter when "hook functions" are called by SP1 //For the intro/game over/game won screens byte mono_sprite0[10]={8,8,0,0,0,0,0,0,0,0}; //Level data format //a-z=8x8 tiles //0-9=16x16 sprites //a=brick //b=nut //c=door // //0=player sprite //2,4,6,8 startpoints of guardian sprites //3,5,7,9 endpoints of guardian sprites //guardians can be horizontal or vertical char *level_data[] = { //level1 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "a 2 b 3 b a" "a a" "a aaaaaaaaaaaaaaa a" "a b 4 b a" "a a" "a aaaaaaaaaaaaaa a" "a 6 b 5 b a" "a a" "a b aaaaaaaaaaaaa a" "a 9 b 8 a" "a a" "a aaaaaaaaaaaaa a" "a0 7 b b c a" "a a" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", //level2 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "a b 2 7 b a" "a aaaaaa aaaaa aaaaaa a" "a a a a" "a a a c a" "a a b a b a" "a b a b a8 9 a" "a a a b a" "a aaaaa a aaaaaa a" "a a4 b 5 a" "a a b a b a" "a a b a" "a b a a a" "a0 a 3 aaaaa 6 aaaaaa a" "a b b a" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", //level3 "aaaaaaaaaaaaaa aaaaaaaaaaaaaa" "a2 b b 3 a" "a a" "a b aaaa aaaa b a" "a b 8 aaaa a" "a aaaa b a" "a b aaaa c a" "a4 5 a" "a b b a" "a a" "a aaaa b aaaa a" "a a" "a b a" "a0 aaaa6 9 b 7 a" "a b a" "aaaaaaaaaaaaaa aaaaaaaaaaaaaa", //level4 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "a b 2 3 c a" "a a a a" "a aaaaaaaaaaaaa 4 b a" "a a a aaa a" "a aaaaa b b a" "a a a aaaa aaa a" "a aaaaa6 7 a" "a a a b b a" "a aaaaa8 aaa aaa a" "a a a b a" "a aaaaa aaa a" "a a a a" "a aaaaa9 5 0 a" "a a a a" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", //level5 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "a 4 b 6 b a" "a bab a" "a c aaa a" "a b a b a" "a aaaaaaa a" "a8 9 a 7 a" "a b a b a" "a 5 aaaaaaaaaaaaa a" "a b bab b a" "a aaaaaaaaaaaaaaaaaaaaaaaaaaa a" "a a 3 b 2 a a" "a a b b a b a" "a0 aaaaaaaaaaaaaaaaaaaaa a" "a a" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", //level6 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "ac 8 6 a" "a b b a" "a b a a a" "a a a b a a" "aaaaaaaa a a a a" "a a a a a" "a 9 a a a a" "a b a a a b a" "a a b a b a a a" "a 0 a a a4 a a" "a a a a b a a" "a a b a a a a" "a a2 3 a 5a 7 a" "a b a b a b a b a" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", //level7 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "a0 b c a" "a a" "aaaaaaaaaa aaaaaaaaaaaaaaaaaaaa" "a2 b 3 a" "ab a" "aaaaaaaaaaaaaaaaaa aaaaaaaaaaaa" "a5 b 4 a" "a b a" "aaaaaaaaa aaaaaaaaaaaaaaaaaaaaa" "a6 b 7 a" "a b b a" "aaaaaaaaaaaaaaaaa aaaaaaaaaaaaa" "a9 8 a" "ab b a" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", //level8 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "ab 8 c 6 b a" "a a b a a" "a b a" "a a b a" "a b b ba a" "a 9 a a" "a5 b 4 a" "a a" "a b a a a" "a2 b 3 a" "a a" "a a b a a" "a 0 7 a" "a b b a" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", //debug level //"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" //"a 8 6 a" //"a a a a" //"a a" //"a a a" //"a a a" //"a 9 a a" //"a5 4 a" //"a a" //"a a a a" //"a2 3 a" //"a a" //"a a a a" //"a c b 0 7 a" //"a a" //"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }; char *level_name[]={ "SPANNER IN THE WORKS", "PIE IN THE OINTMENT", "THE BLACK HOLE", "DIVING BOARD", "AYE AYE CAPTAIN", "THE 39 STEPS", "MAZE DROPPER", "ISLANDS IN THE SKY"}; //number of lives byte lives; //level number byte level; //number of nuts on a single level byte nuts; //don't try this at home //amount of time remaining byte time, time_ticks; //Temp string for status area char tempstr[32]; //Position of door; byte doorrow,doorcol; //16x24 pixel Sprites (mask data followed by sprite data) /* C source file created by SevenuP v1.20 */ /* SevenuP (C) Copyright 2002-2006 by Jaime Tejedor Gomez, aka Metalbrain*/ /* GRAPHIC DATA: Pixel Size: ( 16, 24) Char Size: ( 2, 3) Frames: 8 Sort Priorities: Mask, Char line, Y char, X char, Frame number Data Outputted: Gfx Interleave: Sprite Mask: Yes, before graphic */ //Player character "Sid Spanners" unsigned char sprite_sid1[782] = { //14 bytes interleaved padding 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0, //right0 255, 0,241, 14,224, 31,224, 29, 224, 31,241, 14,192, 63,128,127, 16,239, 48,207,224, 31,192, 63, 204, 51,156, 99,142,113,207, 48, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,237, 18, 76,179, 204, 51,237, 18,243, 12, 99,156, 3,252,147,108,243, 12,243, 12, 179, 76, 63,192, 63,192,127,128, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, //right1 255, 0,241, 14,224, 31,224, 29, 224, 31,241, 14,224, 31,192, 63, 128,111,128,111,192, 63,224, 31, 192, 51,192, 51,224, 27,226, 29, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,127,128, 255, 0,249, 6,243, 12,103,152, 46,209, 36,219, 1,126, 15,112, 31,224, 63,192,127,128, 63,192, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, //right2 255, 0,241, 14,224, 31,224, 29, 224, 31,241, 14,240, 15,240, 15, 240, 15,240, 15,224, 31,224, 31, 249, 6,249, 6,248, 7,248, 7, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,127,128, 255, 0,255, 0,255, 0,255, 0, 115,140, 97,158, 31,224, 31,224, 225, 30,243, 12,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, //right3 255, 0,241, 14,224, 31,224, 29, 224, 31,129,110,128,127,128,127, 192, 63,224, 31,240, 15,224, 31, 224, 27,192, 51,194, 61,226, 29, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,127,128, 255, 0,255, 0,255, 0,255, 0, 255, 0,127,128, 31,224, 15,240, 15,176, 15,144, 31,224, 63,192, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, //left0 255, 0,255, 0,183, 72, 50,205, 51,204,183, 72,207, 48,198, 57, 192, 63,201, 54,207, 48,207, 48, 205, 50,252, 3,252, 3,254, 1, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, 255, 0,143,112, 7,248, 7,184, 7,248,143,112, 3,252, 1,254, 8,247, 12,243, 7,248, 3,252, 51,204, 57,198,113,142,243, 12, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, //left1 255, 0,255, 0,255, 0,254, 1, 255, 0,159, 96,207, 48,230, 25, 116,139, 36,219,128,126,240, 14, 248, 7,252, 3,254, 1,252, 3, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, 255, 0,143,112, 7,248, 7,184, 7,248,143,112, 7,248, 3,252, 1,246, 1,246, 3,252, 7,248, 3,204, 3,204, 7,216, 71,184, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, //left2 255, 0,255, 0,255, 0,254, 1, 255, 0,255, 0,255, 0,255, 0, 206, 49,134,121,248, 7,248, 7, 135,120,207, 48,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, 255, 0,143,112, 7,248, 7,184, 7,248,143,112, 15,240, 15,240, 15,240, 15,240, 7,248, 7,248, 159, 96,159, 96, 31,224, 31,224, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, //left3 255, 0,255, 0,255, 0,254, 1, 255, 0,255, 0,255, 0,255, 0, 255, 0,254, 1,248, 7,240, 15, 240, 13,240, 9,248, 7,252, 3, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, 255, 0,143,112, 7,248, 7,184, 7,248,129,118, 1,254, 1,254, 3,252, 7,248, 15,240, 7,248, 7,216, 3,204, 67,188, 71,184, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0 }; unsigned char sprite_enemy1[782] = { //14 bytes interleaved padding 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0, //enemy0 frame0 255, 0,224, 31,192, 63,192, 63, 192, 63,192, 60,224, 29,240, 15, 248, 7,248, 7,252, 3,248, 7, 248, 7,240, 15,224, 31,248, 7, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, 255, 0,127,128, 15,240, 7,248, 3,252, 3,156, 3,188, 3,252, 3,252, 3,252, 7,248, 7,248, 15,240, 31,224, 63,192,255, 0, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, //enemy0 frame1 255, 0,254, 1,240, 15,224, 31, 192, 63,192, 57,192, 61,192, 63, 192, 63,192, 63,224, 31,224, 31, 240, 15,248, 7,252, 3,255, 0, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, 255, 0, 7,248, 3,252, 3,252, 3,252, 3, 60, 7,184, 15,240, 31,224, 31,224, 63,192, 31,224, 31,224, 15,240, 7,248, 31,224, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, //enemy1 frame0 255, 0,223, 32,207, 48,231, 24, 240, 15,224, 31,224, 31,224, 29, 224, 31,240, 15,229, 26,205, 50, 217, 38,211, 44,223, 32,159, 96, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, 255, 0,247, 8,231, 24,207, 48, 31,224, 7,248, 7,248, 7,184, 7,248, 15,240,175, 80,167, 88, 183, 72, 51,204,251, 4,249, 6, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, //enemy1 frame1 255, 0,239, 16,231, 24,243, 12, 248, 7,224, 31,224, 31,224, 29, 224, 31,240, 15,245, 10,229, 26, 237, 18,204, 51,223, 32,159, 96, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, 255, 0,251, 4,243, 12,231, 24, 15,240, 7,248, 7,248, 7,184, 7,248, 15,240,167, 88,179, 76, 155,100,203, 52,251, 4,249, 6, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, //enemy2 frame0 247, 8,251, 4,253, 2,254, 1, 0,255, 0,128, 0,159, 0,179, 0,185, 0,189, 0,188, 0,190, 0,191, 0,159, 0,128, 0,255, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, 239, 16,223, 32,191, 64,127,128, 0,255, 0, 1, 0,225, 0,245, 0,241, 0,245, 0,241, 0,117, 0, 49, 0,225, 0, 1, 0,255, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, //enemy2 frame1 247, 8,251, 4,253, 2,254, 1, 0,255, 0,128, 0,159, 0,185, 0,188, 0,190, 0,190, 0,191, 0,191, 0,159, 0,128, 0,255, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, 239, 16,223, 32,191, 64,127,128, 0,255, 0, 1, 0,225, 0,245, 0,241, 0,245, 0,113, 0, 53, 0,145, 0,225, 0, 1, 0,255, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, //enemy3 frame0 252, 3,248, 7,252, 3,254, 1, 248, 7,240, 15,224, 31,128,127, 128,120,160, 95,176, 77,176, 79, 28,227, 28,227,240, 15,240, 15, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, 63,192, 31,224, 56,199,120,135, 29,226, 13,242, 5,186, 1,254, 1, 30, 7,248, 15,240, 15,240, 63,192, 63,192, 15,240, 15,240, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, //enemy3 frame1 252, 3,248, 7, 28,227, 30,225, 184, 71,176, 79,160, 93,128,127, 128,120,224, 31,240, 15,240, 15, 252, 3,252, 3,240, 15,240, 15, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0, 63,192, 31,224, 63,192,127,128, 31,224, 15,240, 7,248, 1,254, 1, 30, 5,250, 13,178, 13,242, 56,199, 56,199, 15,240, 15,240, 255, 0,255, 0,255, 0,255, 0, 255, 0,255, 0,255, 0,255, 0}; //Array of pointers to animated frames of sid character unsigned char *anim_sid_left[6]; unsigned char *anim_sid_right[6]; //Array of pointers to "Start sprite frames" of each character //Next frame of guardian sprites is guaranteed to be 96 bytes //later in memory that initial frame unsigned char *start_frame[MAX_SPRITES]; //8x8 tiles byte tile_brick[] = {223,223,223, 0,251,251,251, 0}; byte tile_nut[] = { 0, 60,102, 66, 66,102, 60, 0}; byte tile_door01[] = { 255,128,191,160,175,168,171,170}; byte tile_door02[] = { 255, 1,253, 5,245, 21,213, 85}; byte tile_door03[] = { 170,171,168,175,160,191,128,255}; byte tile_door04[] = { 85,213, 21,245, 5,253, 1,255}; //Define area managed by SP1 lib struct sp1_Rect cr = {0, 0, 32, 16}; //Top two-thirds of the screen only //Rectangles for invalidating 1x1 and 2x2 character squares respectively struct sp1_Rect onecr = {0,0,1,1}; struct sp1_Rect twocr = {0,0,2,2}; //Function prototypes void init_universe(void); void intro_screen(void); void init_game(void); void init_level(byte level); void *u_malloc(uint size); void u_free(void *addr); void zx_border2(byte b); void setcolattr(byte i, byte p, byte f, byte b); void cls(void); void colcls(byte i, byte p, byte f, byte b); void colborcls(byte i, byte p, byte f, byte b, byte o); void setcolmode(byte m); void fatal_error(char *s); void draw_level_background(char *map); void printchrat64(byte x, byte y, char c); #define printchrat32(x,y,s) printchrat64((x)<<1,(y),(s)) void printstrat64(byte x, byte y, char *s); #define printstrat32(x,y,s) printstrat64((x)<<1,(y),(s)) void define_tileset(byte level); void init_sprites(char *map); void init_anim_lists(void); void debug_msg(char *msg); void init_im2_handler(void); void init_keyboard(void); void add_sprite_colour(struct sp1_cs *cs); void my_bit_fx(byte n, byte m); void draw_status_area(void); void draw_level_name(void); void draw_lives(void); void draw_nuts(void); void draw_time(void); void game_over(void); void out_of_time(void); void won_game(void); void print_huge_char(byte x, byte y, byte i, char c); void print_huge_string(byte x, byte y, byte i, char *s); main() { byte level_over,input; byte i,solid_ground,caught; byte cx,cy; byte tx,ty; word j; //An object touched by the player //and the location of it on the map char touched; word touchloc; //Temporary variable which calculates offset into //map of row underneath player characters feet - //used simply to avoid having to multiply sprite //row by 32 more than once in the main game loop word maprowfeet; //for debugging word temp1,temp2; char tempstr[64]; struct sprentry *se; init_universe(); beginning: while(1) { intro_screen(); init_game(); levelstart: init_level(level); level_over=0; while(!level_over) { //Main game loop //has the player been caught by an enemy? caught=0; //Update screen sp1_UpdateNow(); //Calculate absolute position of all sprites for(i=0;ipx=(se->s->col<<3)+(se->s->hrot&7); se->py=(se->s->row<<3)+(se->s->vrot&7); //collision detection //for each enemy sprite - detect if the player //sprite has been touched if(i) { //need (int) casts to produce signed result cx=(byte)ABS((int)(se->px)-(int)(sprtbl[0].px)); cy=(byte)ABS((int)(se->py)-(int)(sprtbl[0].py)); //These values dictate the sensitivity of the collision detection if((cx<12)&&(cy<12)) caught=i; } } //debugging zone //for(temp1=0;temp1<10;temp1++) // delay(10000); //setcolmode(64); //sprintf(tempstr,"%d %d\0",sprtbl[0].px,sprtbl[0].py); //printstrat64(0,0," "); //printstrat64(0,0,tempstr) //Handle Player Character //Set sprite entry pointer to sprite zero (player sprite) se = &sprtbl[0]; //check for picking up a nut maprowfeet=(se->s->row+2)<<5; //Reset variable to no item touched=' '; //disregard checking when player has jumped off the top of the map if(se->py<192) { //check six squares behind the player for(i=0;i<3;i++) for(j=maprowfeet-64;js->col+i]>'a') { touchloc=j+se->s->col+i; touched=current_map[touchloc]; } } //deal with touched nut (enough of that - ed) if(touched=='b') { current_map[touchloc]=' '; ty=touchloc>>5; tx=touchloc&31; sp1_PrintAt(ty,tx,PAPER_BLACK|INK_YELLOW|BRIGHT,' '); onecr.row=ty; onecr.col=tx; sp1_Invalidate(&onecr); sp1_UpdateNow(); nuts--; draw_nuts(); my_bit_fx(1,2); //activate door if no nuts remaining if(!nuts) { my_bit_fx(2,4); sp1_PrintAt(doorrow,doorcol,6|BRIGHT|FLASH,'c'); sp1_PrintAt(doorrow,doorcol+1,6|BRIGHT|FLASH,'d'); sp1_PrintAt(doorrow+1,doorcol,6|BRIGHT|FLASH,'e'); sp1_PrintAt(doorrow+1,doorcol+1,6|BRIGHT|FLASH,'f'); twocr.row=doorrow; twocr.col=doorcol; sp1_Invalidate(&twocr); sp1_UpdateNow(); } } //deal with player entering open door if((!nuts)&&(touched>='c')&&(touched<='f')) { //set level_over to 2 when player succesfully completes level level_over=2; } //deal with player caught if(caught) { zx_border2(2); for(i=0;i<3;i++) my_bit_fx(1,7); zx_border2(0); lives--; se->dx=0; se->dy=0; airborne=0; sp1_MoveSprAbs(se->s, &cr, start_frame[0],se->startrow , se->startcol, 0, 0); draw_lives(); //deal with player dead if(!lives) { //set level_over to 1 when player dies level_over=1; } } //decrement timer if(time_ticks++>50) { time_ticks=0; time--; if(time==255) level_over=3; //out of time else draw_time(); } //solid_ground=((current_map[((se->s->row+2)<<5)+se->s->col]=='a')|| // (current_map[((se->s->row+2)<<5)+se->s->col+1]=='a')|| // ((current_map[((se->s->row+2)<<5)+se->s->col+2]=='a')) //detect a solid surface under player //only check the last tile if the horizontal rotation is non-zero solid_ground=((current_map[maprowfeet+se->s->col]=='a')|| (current_map[maprowfeet+se->s->col+1]=='a')|| ((current_map[maprowfeet+se->s->col+2]=='a'))&&se->s->hrot); //check for walking off the end of a platform if((!solid_ground)&&(!airborne)) { se->dy=1; airborne=1; } //read keyboard input = (getjoy)(&keys); //player walking on a platform if(!airborne) { if((input & in_LEFT)&&(se->px>8)) { facing_left=1; se->dx=-1; //if somehow got stuck inside a platform, then //move back onto the top of it if(se->s->vrot&7) { se->dy=-(se->s->vrot&7); } //animate sprite if(frame_tick++>1) { se->frame++; if(se->frame>5) se->frame=0; frame_tick=0; } } if((input & in_RIGHT)&&(se->px<232)) { facing_left=0; se->dx=1; //if somehow got stuck inside a platform, then //move back onto the top of it if(se->s->vrot&7) { se->dy=-(se->s->vrot&7); } //animate sprite if(frame_tick++>1) { se->frame++; if(se->frame>5) se->frame=0; frame_tick=0; } } if(input & (in_FIRE|in_UP|in_DOWN)) { airborne=1; se->dx=(input & in_RIGHT?1:0)-(input & in_LEFT?1:0); se->dy=-2; } } if(airborne) { //adjust speed every few ticks //The higher this value, the higher sid can jump if(jump_tick++>8) { if(se->dy<2) se->dy++; jump_tick=0; //if speed changed to +2 on an odd line, then adjust to +1 //for one more iteration, to keep the vertical position an //even number and avoid landing inside a platform if((se->dy==2)&&(se->s->vrot&1)) { se->dy=1; //force increment to speed of two on following interation jump_tick=9; } } //landed - check for sold ground, moving downwards and player not off the top of the screen //also check for vrot values of 0,1,2 preventing player getting stuck inside platform //near the top of the jump i=se->s->vrot&7; if(solid_ground&&(se->dy>=0)&&(se->py<192)&&(i<3)) { airborne=0; //nullify residual speed to land exactly on platform se->dy=0; jump_tick=0; } //prevent jumping beyond the sides of the play area if((se->px<9)&&(se->dx==-1)) se->dx=0; if((se->px>231)&&(se->dx==1)) se->dx=0; //Check for player falling through hole in "The Black Hole" level if(se->py>191&&se->py<200) se->s->row=0; } //move player sprite if(facing_left) sp1_MoveSprRel(se->s, &cr, anim_sid_left[se->frame], 0, 0, se->dy, se->dx); //Move sprite else sp1_MoveSprRel(se->s, &cr, anim_sid_right[se->frame], 0, 0, se->dy, se->dx); if(!airborne) { //stop moving if not airborne se->dx=0; se->dy=0; } //move guardian sprites for(i=1;istartrow==se->endrow) { //horizontal guardian if(se->direction==1) { //going from start to end //cast operands to signed values se->dx=SGN((char)se->endcol-(char)se->startcol); //check for end reached if((se->s->col==se->endcol)&&(se->s->hrot==0)) { se->direction=-1; //reverse direction se->dx=0; //stop moving } } else { //going from end to start //cast operands to signed values se->dx=SGN((char)se->startcol-(char)se->endcol); //check for start reached if((se->s->col==se->startcol)&&(se->s->hrot==0)) { se->direction=1; //forward direction se->dx=0; //stop moving } } } else { //vertical guardian if(se->direction==1) { //going from start to end //cast operands to signed values se->dy=SGN((char)se->endrow-(char)se->startrow); //check for end reached if((se->s->row==se->endrow)&&((se->s->vrot&7)==0)) { se->direction=-1; //reverse direction se->dy=0; //stop moving } } else { //going from end to start //cast operands to signed values se->dy=SGN((char)se->startrow-(char)se->endrow); //check for start reached if((se->s->row==se->startrow)&&((se->s->vrot&7)==0)) { se->direction=1; //forward direction se->dy=0; //stop moving } } } //move the sprite sp1_MoveSprRel(se->s, &cr, (guardian_flip?start_frame[i]+96:start_frame[i]), 0, 0, se->dy, se->dx); } //end for() //alternate between two animated frames if(guardian_tick++>15) { guardian_flip^=1; guardian_tick=0; } } //end while(!level_over) //end of level conditions //level_over==1 Player has ran out of lives (game over) //level_over==2 Player has successfully complteted the level (go to next level or win the game) //level_over==3 Player has ran out of time (game over) if(level_over==1) { //game over for(i=0;i<3;i++) my_bit_fx(2,6); game_over(); } else if(level_over==2) { //level completed for(i=0;i<3;i++) my_bit_fx(2,5); if(level++==NUM_LEVELS) { won_game(); } else goto levelstart; } else if(level_over==3) { for(i=0;i<3;i++) my_bit_fx(2,6); out_of_time(); } } //end while(1) } //end main() function void init_universe(void) { struct sp1_ss *s; byte i; //Replace system ISR routine and enter IM2 mode init_im2_handler(); // Setup the heap mallinit(); // set heap as currently empty sbrk(HEAP_ADDR,HEAP_SIZE); // Allocate defined chunk to heap current_map=u_malloc(LEVEL_SIZE); // Allocate space within heap for map data of current level //Initialise SP1 sprite engine sp1_Initialize(SP1_IFLAG_MAKE_ROTTBL | SP1_IFLAG_OVERWRITE_TILES | SP1_IFLAG_OVERWRITE_DFILE, INK_YELLOW|PAPER_BLACK|BRIGHT, ' '); //create sprites for(i=0;i='a') { switch(c) { //brick case 'a': sp1_PrintAt(y,x,2|BRIGHT,'a'); break; //nut case 'b': sp1_PrintAt(y,x,7|BRIGHT,'b'); nuts++; break; //door case 'c': doorrow=y; doorcol=x; sp1_PrintAt(y,x,6|BRIGHT,'c'); sp1_PrintAt(y,x+1,6|BRIGHT,'d'); sp1_PrintAt(y+1,x,6|BRIGHT,'e'); sp1_PrintAt(y+1,x+1,6|BRIGHT,'f'); break; //ignore any undefined characters in map data default: break; } } } sp1_Invalidate(&cr); //Invalidate the area defined by the clipping rectangle sp1_UpdateNow(); //Update (redraw) the area } void printchrat64(byte x, byte y, char c) { putchar(22);putchar(32+y);putchar(32+x);putchar(c); } void printstrat64(byte x, byte y, char *s) { putchar(22);putchar(32+y);putchar(32+x); printf("%s",s); } void define_tileset(byte level) { //Brick sp1_TileEntry('a', tile_brick); //Nut sp1_TileEntry('b',tile_nut); //Door sp1_TileEntry('c',tile_door01); sp1_TileEntry('d',tile_door02); sp1_TileEntry('e',tile_door03); sp1_TileEntry('f',tile_door04); } void init_sprites(char *map) { struct sp1_ss *s; byte row,col,sn; word i; char c; init_anim_lists(); //reset sprite parameters for(i=0;i>5; col=i&31; if((c>='0')&&(c<='9')) { //calculate sprite number sn=(c-'0')>>1; //if even then it's a start position, else an end position if((c-'0')&1) { //odd sprtbl[sn].endcol=col; sprtbl[sn].endrow=row; } else { //even sprtbl[sn].startcol=col; sprtbl[sn].startrow=row; } } } //Colour all sprites, and place them on the screen for(i=0;iattr=sprite_colour[global_n]; cs->attr_mask=0; } } void my_bit_fx(byte n, byte m) { switch(n) { case 1: bit_fx(m); break; case 2: bit_fx2(m); break; case 3: bit_fx3(m); break; case 4: bit_fx4(m); break; default: break; } } void draw_status_area(void) { setcolmode(32); setcolattr(7,0,0,1); draw_level_name(); draw_lives(); draw_nuts(); draw_time(); } void draw_level_name(void) { sprintf(tempstr,"LEVEL %01d: %s",level,level_name[level-1]); printstrat32(0,17,tempstr); } void draw_lives(void) { sprintf(tempstr,"LIVES: %03d ",lives); printstrat32(0,20,tempstr); } void draw_nuts(void) { sprintf(tempstr,"NUTS : %03d ",nuts); printstrat32(0,19,tempstr); } void draw_time(void) { sprintf(tempstr,"TIME : %03d ",time); printstrat32(0,21,tempstr); } void game_over(void) { byte i; //set border zx_border2(0); //clear and initialise low res screen cclg(0); print_huge_string(0,16,2,"GAME"); print_huge_string(0,24,2,"OVER"); //Play short noise for(i=0;i<12;i++) my_bit_fx(4,2); WAIT_KEY; } void out_of_time(void) { byte i; //set border zx_border2(0); //clear and initialise low res screen cclg(0); print_huge_string(4,12,2,"OUT"); print_huge_string(8,20,2,"OF"); print_huge_string(0,28,2,"TIME"); //Play short noise for(i=0;i<12;i++) my_bit_fx(4,2); WAIT_KEY; } void won_game(void) { byte i; //set border zx_border2(0); //clear and initialise low res screen cclg(0); print_huge_string(0,8,6,"CONG"); print_huge_string(0,16,6,"RATS"); print_huge_string(0,24,4,"GAME"); print_huge_string(0,32,4,"WON!"); //Play short noise for(i=0;i<3;i++) my_bit_fx(4,1); WAIT_KEY; } void print_huge_char(byte x, byte y, byte i, char c) { word k; //get base address of character from ROM k=0x3d00+((c-32)<<3); //copy character from ROM to "monochrome sprite" array memcpy(&mono_sprite0[2],(byte *)k,8); cputsprite(i,x,y,mono_sprite0); } void print_huge_string(byte x, byte y, byte i, char *s) { char c; while(c=*s++) { print_huge_char(x,y,i,c); x+=8; } }