/*Smiler 5: The Scattered Runes Version 1.0 Digital Prawn November 2010 Public Domain*/ #include #include #include #include #include #include #include #include //For debugging/playtesting #define STARTING_LEVEL 1 //For the demo version //#define DEMO_VERSION /* Memory Map 23755 ZX BASIC loader 24576 Bank of dormant AY tunes in contended RAM 32600 (approx) potential lower limit of stack 32768 Compiled C code and data (can reach up to 56025 MAX - Max code filesize = 23258) 56026 (0xDADA) 3-byte JP instruction to (compiled C) ISR routine 56064 (0xDB00) 257-byte IM2 vector table filled with 0xDADA 56321 vtplayer Chip music player (1617 bytes) 58479 Currently selected AY Chip tune (typically less than 2K bytes) 63488 1K HEAP for malloc() requests. (Could reach max range of 60527-64511) 64512 UDGs Vortex Tracker II - must export with player address of 56321 56321=AY_INIT() 56326=AY_PLAY() 56329=AY_MUTE() Extra AY chip music tunes -> low RAM. Copied into high RAM before playing. */ //If defined then compile code for AY chip music //which must be appended onto the tapefile #define AY_VERSION #ifdef AY_VERSION #define NUM_TUNES 6 //number of AY tunes loaded #endif #define LEVEL_SIZE 176 #define HEAP_ADDR 63488 #define UDG_ADDR 64512 #define WAIT_KEY while(!in_Inkey()) #ifdef DEMO_VERSION #define NUM_LEVELS 1 #else #define NUM_LEVELS 20 #endif //Maximum number of antagonists per level #define MAX_ANTAGONISTS 16 //MACROS #define SGN(x) ((x)<0?-1:((x)>0?1:0)) typedef unsigned char byte; typedef unsigned int word; //Globals //Colours of arrows byte arrow_colour[4]={7,3,2,1}; //addresses and lengths of all loaded tunes #ifdef AY_VERSION word tune_addr[NUM_TUNES]={28975,29986,24576,25675,27167,27843}; word tune_len[NUM_TUNES]={1011,1400,1099,1057,676,1132}; #endif //level passcodes char *passcode[]= { "744088", "200965", "689796", "775803", "626180", "754174", "217403", "272163", "743543", "807725", "712359", "790432", "851270", "566010", "322216", "988113", "282455", "931050", "026850", "884335", "320837" //Extra passcode for "anagram screen" }; // LAST_K system variable used by IM2 keyboard handling routine extern char LAST_K(23560); // variables used by in_GetKey() byte in_KeyDebounce = 1; byte in_KeyStartRepeat = 20; byte in_KeyRepeatPeriod = 10; word in_KbdState; byte music_on=0; long heap; char *current_map; /* level map data format (case sensitive) ' ' = open space '#' = red house obstruction 'S' = smiler starting point 'G' = grinner starting point (Smiler's Cousin) 'H' = Green house, the object of each level 'R' = Rune, needed to activate house and solve final anagram (All arrows are listed in the sequence North, East, South, West) 'd','e','f','g' = Magenta arrows 'h','i','j','k' = Red arrows 'l','m','n','o' = Blue arrows 'p','q','r','s' = White arrows 't','u','v',w' = Reserved for future arrows 'x','y','z','{' = Reserved for future arrows a = antagonist (Frowner) */ /* Arrow behaviour Magenta arrows - same as first Smiler game - rotate all arrows on the level 90 degrees clockwise Red arrows - rotate all arrows on the level 90 degrees anti-clockwise Blue arrows - rotate all blue arrows 180 degrees. Rotate all magenta arrows 90 degrees clockwise. Rotate all red arrows 90 degrees anti-clockwise. Do not rotate white arrows. White arrows - do not rotate any arrows - but kill frowners when they step onto them */ /* level map data */ char *map[]= { "################" //level 1 "# # # #" "# # # #" "#d### kjf ### e#" "#G#a# iRo #a#lS#" "#f# # gln # # g#" "# d f #" "#de#fg#o#ed#fgd#" "# lfghn #" "# kjHim #" "################", "################" //level 2 "#a l n m oaH#" "##ed##h##j##k###" "#Rql m n o #" "###kj##i##h##gf#" "# ro n m sl#" "#k##j##i##k##g##" "#o n m l n #" "#gf##k##j##ki###" "# n m n gSG#" "################", "################" //level 3 "#Sm m m oG#" "#f##djg##djgig##" "# ##n n##n ##" "# r## s## #" "#jikl##hklj##hl#" "#a n n a#" "###kl##jl##mn###" "### p##q ## ###" "#Ra lm lo aoH#" "################", "################" //level 4 "#aRe j l aH#" "#i# # hj#ef# #" "# ldf # n e#" "# # ej#o #" "#op#kl ld n#q#" "# # ji# #" "#ld#ij d# e #o#" "#Sm # fgnG#" "################", "################" //level 5 "# j R l #" "# adg klj ija #" "#kme########khd#" "# #df ki# #" "# q# SHG #p #" "#eel# #iij#" "# d odgjkldm d #" "# kf nn ik #" "# kag ie jak #" "################", "################" //level 6 "#fe g n k kh#" "# #noS f Gml# #" "#d#ef#kjkg#fe# #" "# # #r s# # #" "# #hiljjkesnlgi#" "#e m q #j#" "#k#lm#fiop#mo# #" "# # #glen# # #" "#H#aa dg aa#R#" "################", "################" //level 7 "#a ih hmnfa#" "#heej#dkj# o #" "# r# #sg #" "#df#ghkl#fd#ghj#" "#R# d #G# # H#" "## lom# # ###" "#d# p# fdi #s S#" "#fgh#jiklfed#dj#" "#a e i a#" "################", "################" //level 8 "#Sn mi l en eH#" "#jg # rd p# ki#" "# #d ## e # #" "#l# f aha e #o#" "#ookl#jkjg#gihd#" "#n# qgaia #i#" "#e # i ## # #" "#ik #kfejkl# gh#" "#Gefd sf ejkfR#" "################", "################" //level 9 "# m lsG sS e #" "#d##gj##kl##jkm#" "# ## a##a ## #" "# ##mh##lo##dfk#" "#i##kk##df##lme#" "#k jgh khj d k#" "#i####dH####klo#" "#q####fg####mde#" "# j a R a g s #" "################", "################" //level 10 "#fea f a##R #" "#e##a d aj## #" "# ##klo##g ##h#" "#n ## ## p i#" "# d fjk ## g#" "#d## q ##mkl g#" "#G ## ##r s#" "# H## g## omd#" "#S ed e df #" "################", "################" //level 11 "# g f a h f a #" "#f#####ed lghoh#" "# re S#Hf f s #" "# e ######ldd#" "#iejkd#G l # e #" "# p #fgj # q #" "#mjgdlmngdf#eng#" "# h i # #" "#Ra k l gfia #" "################", "################" //level 12 "# m # al # H#" "#l###k#n###d#oe#" "# # sh # pma #" "#fd#g###k#o###e#" "# fa # g # #" "#h###j#h###g#df#" "# G# rj # qe #" "# S#e###g#h###m#" "#l i #a d #R #" "################", "################" //level 13 "# l djaRakg m #" "#k############i#" "# d# e # o # e #" "# j q#l#H#s# i##" "#fd### # k #gj #" "# m s#f f#q #h#" "# # e # #m #" "#l############n#" "#Ge jaah dS#" "################", "################" //level 14 "# # Sj #nfR#" "# p g # ehgj#" "##n###l###n###o#" "# f # #G #" "# a# e f #" "##d###l###o###n#" "# # a# # #" "# m j gq #" "#H # # # #" "################", "################" //level 15 "# d # erhojl#" "# feR#efk# f #" "#e#ghj# #djlk#" "# # p #ljk# a #" "## #fgj# a#df#" "#l# # q #lid#H#" "#SG#g # #n ##" "#dee#oi#gjh#dfl#" "# flgh# e #" "################", "################" //level 16 "#Rj # # #" "#ki afjika# s #" "# r # sjlgled #" "# o # # ag #" "##e##########h##" "# l a#o G# o #" "# n # a #lj #" "# q dl ph# nm#" "#fkj # S# amH#" "################", "################" //level 17 "# e # ji # #" "# # # #R #ni#" "#k###emh###f f#" "# H# a# a# # h#" "# #j###i#k###d#" "#ed fp# g s# #" "# # k#e # f#md#" "#j### l###dh G#" "# # e # gkSn#" "################", "################" //level 18 "# djl kes #" "#erg###H### kld#" "#r # k e # #" "#d# ia aj #ef#" "#s #fjm dlo# q #" "# #e a a h# #" "# d# ### #lno#" "# #s#egqae# p #" "#Sk# #R# # jG#" "################", "################" //level 19 "# a a oR# #" "#ke#djg#hkl#hed#" "# # # # #" "#sqee# #ed #ml #" "# # rG# a#" "#dg# # #djf#j#" "# # # # # #" "#simme#frd#jf #" "#S # l e #Ha#" "################", "################" //level 20 "#SegglifedjklnG#" "#eqghkledfeghsm#" "#lardglhjkinpam#" "#dfaqggeiklrane#" "#fdkaslmedpadgh#" "#dfloapmeqadflk#" "#geedoarsamnedf#" "#dfkilgaaijlkoe#" "#RhoomnadfeghoH#" "################" }; char *level_name[]= { "The Code Trail Begins", "Dark Heart of Sodor", "The Paperless Level", "Into the Magic Square", "The Cosmic Crunch-in", "Phase Modulation", "Onions for Rent", "Fool's Masterplan", "Jolly Good Miffle", "Halfway to Valhalla", "Radio-friendly Gig", "Pass the Taters", "Of Smiles and Grins", "Over the Moonbow", "Thirty Fathoms Deep", "Down Deep Dungeon", "Lost in Transit", "1928 Time Traveller", "Willkommen am Klub", "Serious Predicament" }; //Anagram char anagram[]="snhpbecneronselmisei"; char solution[]="incomprehensibleness"; //Which player character, smiler or grinner? //0=Smiler, 1=Grinner byte current_p; //player x and y and old values (Smiler & Grinner) byte px[2],py[2],ox[2],oy[2],is_home[2]; //anatagonist x and y and old values //if ax[i] is set to zero then it means antagonist is dead (not drawn) byte ax[MAX_ANTAGONISTS],ay[MAX_ANTAGONISTS], oax[MAX_ANTAGONISTS],oay[MAX_ANTAGONISTS], num_antagonists,ant_motion; //Found rune? byte found_rune; /*Set up a UDG (numbered from 128-255) */ void set_udg(byte n,byte d0, byte d1, byte d2, byte d3, byte d4, byte d5, byte d6, byte d7); /*Print a string at x,y */ void printstrat64(byte x, byte y, char *s); #define printstrat32(x,y,s) printstrat64((x)<<1,(y),(s)) /*Set the current colour attributes */ void setcolattr(byte i, byte p, byte f, byte b); /* Print a colour string at x,y */ void printcolstrat64(byte i, byte p, byte f, byte b, byte x, byte y, char *s); #define printcolstrat32(i,p,f,b,x,y,s) printcolstrat64((i),(p),(f),(b),(x)<<1,(y),(s)) /* print a 2x2 UDG number u at x,y */ void print2x2udgat64(byte x, byte y, byte u); #define print2x2udgat32(x,y,u) print2x2udgat64((x)<<1,(y),(u)) /* print a colour 2x2 UDG number u at x,y */ void printcol2x2udgat64(byte i, byte p, byte f, byte b, byte x, byte y, byte u); #define printcol2x2udgat32(i,p,f,b,x,y,u) printcol2x2udgat64((i),(p),(f),(b),(x)<<1,(y),(u)) /* clear the screen */ void cls(void); /* clear the screen to a specific colour */ void colcls(byte i, byte p, byte f, byte b); /* clear the screen and set the BORDER to the specified colour o*/ void colborcls(byte i, byte p, byte f, byte b, byte o); /* set mode to either 32 or 64 columns */ void setcolmode(byte m); /* init the session */ void init_session(void); /* init the game */ void init_game(void); /*init the level */ void init_level(byte l); /*called in the event of a fatal error */ void bombout(char *s); /* redraw the map pointed to by m*/ void redraw_map(char *m); /* draw the outine of a map */ void draw_map_outline(char *m); /* move antagonists around map. Return true if an antagonist catches the player */ byte move_antagonists(char *m); /*semi-optimised version of above function which is called many times in the main game loop*/ void fast_printcol2x2udgat64(byte i, byte p, byte x, byte y, byte u); #define fast_printcol2x2udgat32(i,p,x,y,u) fast_printcol2x2udgat64((i),(p),(x)<<1,(y),(u)); /*rotate the arrows on the board */ void rotate_arrows(char *m, byte rotator); /*Print the intro screen*/ void intro_screen(void); /* Set the status line */ void set_status_line(char *s); /* Wrapper around zx_border() library function. zx_border2() also sets BORDCR system variable which is needed if sounds are played when the BORDER is non-white */ void zx_border2(byte b); /*Slight delay in main loop to reduce flicker*/ void main_loop_delay(byte n); /*Initiate the IM2 mode interrupt handler*/ void init_im2_handler(void); /*Play sound effects but only if music is off. Also sleep for s seconds only if music is on. (to provide equivalent delay to that of a sound effect - so status line can be read) */ void my_bit_fx(byte n, byte m, byte s); //Screen for entering level passcodes byte passcode_screen(void); //Set a 2x2 udg to an enlarged character void set_bigchar_udg(byte n, byte c); //Expand a row of character bitmap into twice it's normal width void expand_char_row(byte in, byte *left, byte *right); //Screen for entering final anagram solution //returns 0 for incorrect answer, 1 for correct answer, //2 to cancel back to main menu byte anagram_screen(void); #ifdef AY_VERSION void select_ay_tune(byte t); #endif //Interrupt service routine for AY chip music M_BEGIN_ISR(isr) { //char c; // if ((c = in_GetKey()) != 0) // LAST_K = c; #ifdef AY_VERSION if(music_on) { #asm call 56326 #endasm } #endif } M_END_ISR main() { byte i,level_over, session_over,level,killed; byte rotate_flag=0; init_session(); roswell: session_over=0; level=STARTING_LEVEL; //for playtesting //level=NUM_LEVELS; while(!session_over) { intro_screen(); init_game(); passenter: level=passcode_screen(); if(level==21) goto anagram; levelstart: init_level(level); level_over=0; redraw_map(current_map); // main game loop while(!level_over) { byte k,nx,ny,c,ok; char temp[32]; //Delays not needed with new smiler/map draw sequence //Slight delay //main_loop_delay(16); //halt to syn with screen raster //#asm //halt //#endasm //keep this commented out to enable keyboard auto-repeat //in_WaitForNoKey(); while(!(k=in_Inkey())); //'x' key quits the game if(k=='x') goto roswell; if(k=='r') { set_status_line("Restarting level!"); my_bit_fx(4,3,2); level_over=1; } //Switch between Smiler and Grinner if(k=='s') { //update old-coordinates to prevent them erasing //smiler being switched to ox[current_p]=px[current_p]; oy[current_p]=py[current_p]; current_p^=1; redraw_map(current_map); my_bit_fx(1,5,0); } #ifdef AY_VERSION if(k=='0') { music_on++; if(music_on>NUM_TUNES) music_on=0; if(!music_on) { #asm call 56329; #endasm } if(music_on) { select_ay_tune(music_on); } } #endif //store old position values for erasing old sprite in draw routine ox[current_p]=px[current_p];oy[current_p]=py[current_p]; nx=px[current_p]+(((k=='p')-(k=='o'))<<1); ny=py[current_p]+(((k=='a')-(k=='q'))<<1); c=current_map[(ny<<3)+(nx>>1)]; //Debugging //sprintf(temp,"%d %d %d %d %d \0",px,py,nx,ny,(int)c); //printstrat32(0,18,temp) ok=0; if(c>='a'&&c<='z') { byte d=c&0x03; ok=((d==0)&&(k=='q'))||((d==1)&&(k=='p'))|| ((d==2)&&(k=='a'))||((d==3)&&(k=='o')); } rotate_flag=0; //if can move into square and have actually moved on this turn if((c==32||c=='H'||c=='R'||ok)&&(px[current_p]!=nx||py[current_p]!=ny)) { //set new value px[current_p]=nx;py[current_p]=ny; //Check if stepped onto a frowner for(i=0,killed=0;i>1)]=32; my_bit_fx(1,1,0); } } //if smiler succesfully moved then allow antagonists to move //also check if player is killed if(move_antagonists(current_map)) { redraw_map(current_map); set_status_line("A frowner caught you!"); my_bit_fx(2,7,2); level_over=1; } //only redraw the map if smiler moved redraw_map(current_map); //play arrow sound fx immediately after map redraw if(rotate_flag) my_bit_fx(1,6,0); } if(level<=NUM_LEVELS) { goto levelstart; } else { anagram: //Don't have the anagram screen in the demo version #ifndef DEMO_VERSION //repeat anagram screen until correct soultion is presented while(!(i=anagram_screen())); //check if anagram screen was cancelled if(i==2) goto roswell; set_status_line("Congrats! Entire game completed!"); #else set_status_line("Congrats! Demo completed! "); #endif for(i=0;i<3;i++) my_bit_fx(2,4,2); printf("PRESS A KEY"); WAIT_KEY; goto roswell; } } } void set_udg(byte n, byte d0, byte d1, byte d2, byte d3, byte d4, byte d5, byte d6, byte d7) { byte *a=(byte *)UDG_ADDR+((n-128)<<3); a[0]=d0;a[1]=d1;a[2]=d2;a[3]=d3; a[4]=d4;a[5]=d5;a[6]=d6;a[7]=d7; } void printstrat64(byte x, byte y, char *s) { putchar(22);putchar(32+y);putchar(32+x); printf("%s",s); } void setcolattr(byte i, byte p, byte f, byte b) { putchar(16); putchar(i|0x30); //ink putchar(17); putchar(p|0x30); //paper putchar(18); putchar(f|0x30); //flash putchar(19); putchar(b|0x30); //bright } void printcolstrat64(byte i, byte p, byte f, byte b, byte x, byte y, char *s) { setcolattr(i,p,f,b); printstrat64(x,y,s); } void print2x2udgat64(byte x, byte y, byte u) { char t[3]; t[2]=0; t[0]=u++;t[1]=u++; putchar(22);putchar(32+y);putchar(32+x); printf("%s",t); putchar(22);putchar(33+y);putchar(32+x); t[0]=u++;t[1]=u++; printf("%s",t); } void printcol2x2udgat64(byte i, byte p, byte f, byte b, byte x, byte y, byte u) { setcolattr(i,p,f,b); print2x2udgat64(x,y,u); } void fast_printcol2x2udgat64(byte i, byte p, byte x, byte y, byte u) { putchar(16); putchar(i|0x30); //ink putchar(17); putchar(p|0x30); //paper putchar(22);putchar(32+y);putchar(32+x);putchar(u++);putchar(u++); putchar(22);putchar(33+y);putchar(32+x);putchar(u++);putchar(u++); } void cls(void) { putchar(12); //send a form feed control } void colcls(byte i, byte p, byte f, byte b) { setcolattr(i,p,f,b); cls(); } void colborcls(byte i, byte p, byte f, byte b, byte o) { colcls(i,p,f,b); zx_border2(o); } void setcolmode(byte m) { putchar(1);putchar(m); } void init_session(void) { //Inititialise the heap mallinit(); // set heap as currently empty sbrk(HEAP_ADDR,1024); // add a 1K heap // Init keyboard routines in_GetKeyReset(); //Init the sound system bit_open(); //Init the IM2 interrupt handler for keyboard handling & AY chip music init_im2_handler(); //Alter UDG address to allow full range of 128 UDGs (above heap) putchar(3);putchar(UDG_ADDR/0x0100);putchar(UDG_ADDR%0x0100); //setup UDGs //house sprite set_udg(128,1,3,7,15,31,63,127,255); set_udg(129,128,192,224,240,248,252,254,255); set_udg(130,195,195,195,195,255,255,255,255); set_udg(131,255,255,15,15,15,15,15,15); //apartment sprite //set_udg(128,0x00,0x7f,0x7f,0x49,0x49,0x7f,0x49,0x49); //set_udg(129,0x00,0xfe,0xfe,0x92,0x92,0xfe,0x92,0x92); //set_udg(130,0x7f,0x49,0x49,0x7f,0x7f,0x7e,0x7e,0x7e); //set_udg(131,0xfe,0x92,0x92,0xfe,0xfe,0x7e,0x7e,0x7e); //up-arrow sprite set_udg(132,0xff,0xfe,0xfc,0xf8,0xf0,0xe0,0xc0,0xc0); set_udg(133,0xff,0x7f,0x3f,0x1f,0x0f,0x07,0x03,0x03); set_udg(134,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc,0xff,0xff); set_udg(135,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0xff,0xff); //right-arrow sprite set_udg(136,0xff,0xff,0xff,0xff,0xff,0xff,0xc0,0xc0); set_udg(137,0xff,0xff,0x3f,0x1f,0x0f,0x07,0x03,0x01); set_udg(138,0xc0,0xc0,0xff,0xff,0xff,0xff,0xff,0xff); set_udg(139,0x01,0x03,0x07,0x0f,0x1f,0x3f,0xff,0xff); //down-arrow sprite set_udg(140,0xff,0xff,0xfc,0xfc,0xfc,0xfc,0xfc,0xfc); set_udg(141,0xff,0xff,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f); set_udg(142,0xc0,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff); set_udg(143,0x03,0x03,0x07,0x0f,0x1f,0x3f,0x7f,0xff); //left-arrow sprite set_udg(144,0xff,0xff,0xfc,0xf8,0xf0,0xe0,0xc0,0x80); set_udg(145,0xff,0xff,0xff,0xff,0xff,0xff,0x03,0x03); set_udg(146,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xff,0xff); set_udg(147,0x03,0x03,0xff,0xff,0xff,0xff,0xff,0xff); //Smiley face set_udg(148,0xff,0xfc,0xe0,0xc0,0xc0,0xcc,0x8c,0x80); set_udg(149,0xff,0x3f,0x07,0x03,0x03,0x33,0x31,0x01); set_udg(150,0x81,0x91,0xd0,0xc8,0xc6,0xe1,0xfc,0xff); set_udg(151,0x81,0x89,0x0b,0x13,0x63,0x87,0x3f,0xff); //Smiley face (greyed out) set_udg(152,0xff|0xaa,0xfc|0x55,0xe0|0xaa,0xc0|0x55,0xc0|0xaa,0xcc|0x55,0x8c|0xaa,0x80|0x55); set_udg(153,0xff|0xaa,0x3f|0x55,0x07|0xaa,0x03|0x55,0x03|0xaa,0x33|0x55,0x31|0xaa,0x01|0x55); set_udg(154,0x81|0xaa,0x91|0x55,0xd0|0xaa,0xc8|0x55,0xc6|0xaa,0xe1|0x55,0xfc|0xaa,0xff|0x55); set_udg(155,0x81|0xaa,0x89|0x55,0x0b|0xaa,0x13|0x55,0x63|0xaa,0x87|0x55,0x3f|0xaa,0xff|0x55); //Frowner (antagonist) set_udg(156,0xff,0xfc,0xe0,0xc0,0xc0,0xcc,0x8c,0x81); set_udg(157,0xff,0x3f,0x07,0x03,0x03,0x33,0x31,0x81); set_udg(158,0x81,0x80,0xc7,0xc8,0xc0,0xe0,0xfc,0xff); set_udg(159,0x81,0x01,0xe3,0x13,0x03,0x07,0x3f,0xff); //inactive house sprite set_udg(160, 1&0xaa, 3&0x55, 7&0xaa, 15&0x55, 31&0xaa, 63&0x55,127&0xaa,255&0x55); set_udg(161,128&0xaa,192&0x55,224&0xaa,240&0x55,248&0xaa,252&0x55,254&0xaa,255&0x55); set_udg(162,195&0xaa,195&0x55,195&0xaa,195&0x55,255&0xaa,255&0x55,255&0xaa,255&0x55); set_udg(163,255&0xaa,255&0x55, 15&0xaa, 15&0x55, 15&0xaa, 15&0x55, 15&0xaa, 15&0x55); //blank sprite set_udg(252,0,0,0,0,0,0,0,0); set_udg(253,0,0,0,0,0,0,0,0); set_udg(254,0,0,0,0,0,0,0,0); set_udg(255,0,0,0,0,0,0,0,0); //Set 32 column mode setcolmode(32); // Set the screen to black background and border colborcls(0,0,0,0,0); //Allocate memory for current map if(!(current_map=malloc(LEVEL_SIZE))) { bombout("Insufficient RAM"); } } void init_game(void) { //Set 32 column mode for main game setcolmode(32); } void init_level(byte l) { char s[32]; // Set the screen to black background and black border //and clear the screen colborcls(0,0,0,0,0); //Reset found_rune found_rune=0; //Set the large UDG 164 to current letter of anagram //(Converted to upper case) set_bigchar_udg(164,anagram[l-1]&0xdf); memcpy(current_map, map[l-1],LEVEL_SIZE); sprintf(s,"Level %d: %s\0",l,level_name[l-1]); set_status_line(s); printstrat32(0,23,"Code:"); printstrat32(5,23,passcode[l-1]); draw_map_outline(current_map); //reset antagonist motion behaviour ant_motion=0; //Set current sprite to Smiler current_p=0; //Reset is_home? flags is_home[0]=is_home[1]=0; } void bombout(char *s) { setcolmode(64); colborcls(1,0,0,1,0); puts("Fatal Error:"); puts(s); exit(0); } void redraw_map(char *m) { //of=occupied flag byte i,x,y,of; //skip top and bottom for(i=16;i<160;i++) { x=(i&0x0f)<<1; y=(i>>3)&0xfe; of=0; if(x==px[0]&&y==py[0]) { //Erase old smiler if moved off a space if((ox[0]!=x||oy[0]!=y)&&(m[(oy[0]<<3)+(ox[0]>>1)]==32)) { //check for other smiler if(ox[0]==px[1]&&oy[0]==py[1]) { fast_printcol2x2udgat32(0,1,ox[0],oy[0],152); } else { fast_printcol2x2udgat32(0,0,ox[0],oy[0],252); //blank space } } //Draw Smiler fast_printcol2x2udgat32(0,(current_p?1:6),x,y,(current_p?152:148)); of=1; } if(x==px[1]&&y==py[1]) { //Erase old grinner if moved off a space if((ox[1]!=x||oy[1]!=y)&&(m[(oy[1]<<3)+(ox[1]>>1)]==32)) { //check for other smiler if(ox[1]==px[0]&&oy[1]==py[0]) { fast_printcol2x2udgat32(0,1,ox[1],oy[1],152); } else { fast_printcol2x2udgat32(0,0,ox[1],oy[1],252); //blank space } } //Draw Grinner fast_printcol2x2udgat32(0,(current_p?4:1),x,y,(current_p?148:152)); of=1; } if(!of) { //print a map tile or home //Probably not much faster to skip edges //if((x>0x00)&&(x<0x1e) //All lower case letters are arrows if(m[i]>='a'&&m[i]<='z') { fast_printcol2x2udgat32(5,arrow_colour[(m[i]&0x0f)>>2],x,y,132+((m[i]&0x03)<<2)); //arrow } else if(m[i]=='H') { fast_printcol2x2udgat32(4,0,x,y,found_rune?128:160); } } } //draw antagonists for(i=0;i>1)]==32)) fast_printcol2x2udgat32(0,0,oax[i],oay[i],252); //blank space //Draw antagonist fast_printcol2x2udgat32(0,2+(i&1),ax[i],ay[i],156); } } } void draw_map_outline(char *m) { byte i,x,y; num_antagonists=0; for(i=0;i<32;i+=2) { fast_printcol2x2udgat32(2,0,i,0,128); fast_printcol2x2udgat32(2,0,i,20,128); fast_printcol2x2udgat32(2,0,0,(i<20?i:0),128); fast_printcol2x2udgat32(2,0,30,(i<20?i:0),128); } for(i=16;i<160;i++) { x=(i&0x0f)<<1; y=(i>>3)&0xfe; //print just the houses/buildings here //skip map edges for speedup if((x>0x00)&&(x<0x1e)) { switch(m[i]) { case '#': fast_printcol2x2udgat32(2,0,x,y,128); //house break; case 'S': //smiley man ox[0]=px[0]=x; //set starting position oy[0]=py[0]=y; m[i]=' '; //blank the smiley off the current map break; case 'G': //Grinner ox[1]=px[1]=x; //set starting position oy[1]=py[1]=y; m[i]=' '; //blank Grinner off the current map break; case 'R': //Rune fast_printcol2x2udgat32(7,0,x,y,164); //Rune break; // now printed in redraw_map()as it is possible to step off "home" // in this game //case 'H': // //home // fast_printcol2x2udgat32(4,0,x,y,128); // break; case 'a': //antagonist oax[num_antagonists]=ax[num_antagonists]=x; //set starting position oay[num_antagonists]=ay[num_antagonists]=y; m[i]=' '; //blank antagonist off current map num_antagonists++; //next antagonist if(num_antagonists>MAX_ANTAGONISTS) bombout("Too many antagonists on level - increase MAX_ANTAGONISTS"); break; default: break; } } } } void rotate_arrows(char *m, byte rotator) { byte i; switch(rotator) { //the clockwise rotators case 'd':case 'e':case 'f':case 'g': for(i=16;i='a'&&m[i]<='z') m[i]=(m[i]&0xfc)+((m[i]+1)&3); break; //the anti-clockwise rotators case 'h':case 'i':case 'j':case 'k': for(i=16;i='a'&&m[i]<='z') m[i]=(m[i]&0xfc)+((m[i]-1)&3); break; //these rotators turn themselves 180deg and also // turn defg clockwise and hijk anti-clockwise //leave pqrs untouched case 'l':case 'm':case 'n':case 'o': for(i=16;i>1)]; //Check for arrow move ok=0; if(c>='a'&&c<='z') { byte d=c&0x03; ok=((d==0)&&(dy==-2))||((d==1)&&(dx==2))|| ((d==2)&&(dy==2))||((d==3)&&(dx==-2)); } //check for no other antagonists on target square noa=1; for(j=0;j='p'&&c<='s') { redraw_map(m); ax[i]=0; //de-activate antagonist my_bit_fx(1,1,1); } } } return killed; } byte passcode_screen(void) { char buf[7]; byte i,k,match; setcolmode(32); colborcls(7,0,0,0,0); //clear screen and set border printstrat32(4,10,"Enter level Passcode:"); printstrat32(1,14,"or press ENTER for new game"); for(i=0;i<6;i++) printstrat32(9+(i<<1),12,"*"); i=0; while(i<6) { in_WaitForNoKey(); k=0; while(!k) { k=in_Inkey(); //if ENTER pressed then just start on level 1 if(k==13) return 1; //allow only numeric keys and delete if(((k<'0')||(k>'9'))&&(k!=12)) k=0; } //debugging //printf("%d",k); if(k==12) { if(i) i--; printstrat32(9+(i<<1),12,"*"); //my_bit_fx(1,1,0); } else { buf[i]=k;buf[i+1]=0; printstrat32(9+(i<<1),12,buf+i); //my_bit_fx(1,6,0); i++; } } #ifdef DEMO_VERSION return 1; #endif match=0; i=0; //Search 1 more than NUM_LEVELS because the final "anagram screen" //also has a passcode while(i<(NUM_LEVELS+1)&&!match) { if(!strcmp(buf,passcode[i])) { match=i; } i++; } return match+1; } #ifdef AY_VERSION void select_ay_tune(byte t) { // interrupts off & mute #asm di call 56329 #endasm //copy the selected AY tune into the area used by the player memcpy((byte *)58479,(byte *)tune_addr[t-1],tune_len[t-1]); //re-init vtplayer and interrputs back on #asm call 56321 ei #endasm } #endif void set_bigchar_udg(byte n, byte c) { byte *chrptr=(byte *)(0x3d00+((c-32)<<3)); byte t0,t1,t2,t3,t4,t5,t6,t7; expand_char_row(*chrptr++,&t0,&t1); expand_char_row(*chrptr++,&t2,&t3); expand_char_row(*chrptr++,&t4,&t5); expand_char_row(*chrptr++,&t6,&t7); set_udg(n++,t0,t0,t2,t2,t4,t4,t6,t6); set_udg(n++,t1,t1,t3,t3,t5,t5,t7,t7); expand_char_row(*chrptr++,&t0,&t1); expand_char_row(*chrptr++,&t2,&t3); expand_char_row(*chrptr++,&t4,&t5); expand_char_row(*chrptr++,&t6,&t7); set_udg(n++,t0,t0,t2,t2,t4,t4,t6,t6); set_udg(n++,t1,t1,t3,t3,t5,t5,t7,t7); } void expand_char_row(byte in, byte *left, byte *right) { byte i; word w=0; for(i=0;i<8;i++) { if(in&(1<>8; *right=w&0xff; } byte anagram_screen(void) { char buf[21]; byte i,k; setcolmode(32); colborcls(7,2,0,0,2); //clear screen and set border printstrat32(1,10,"Type the anagram solution:"); printstrat32(4,12,"--------------------"); printstrat32(4,14,"Press ENTER to abort"); printstrat32(0,23,"Code:"); printstrat32(5,23,passcode[20]); i=0; while(i<20) { in_WaitForNoKey(); k=0; while(!k) { k=in_Inkey(); //Allow ENTER to cancel the anagram screen if(k==13) return 2; //allow only lowercase alphabetical keys and delete if(((k<'a')||(k>'z'))&&(k!=12)) k=0; } if(k==12) { if(i) i--; printstrat32(4+i,12,"-"); //my_bit_fx(1,1,0); } else { buf[i]=k;buf[i+1]=0; printstrat32(4+i,12,buf+i); //my_bit_fx(1,6,0); i++; } } return(strcmp(buf,solution)==0); }