//video gen and sound //D.5 is sync:1000 ohm + diode to 75 ohm resistor //D.6 is video:330 ohm + diode to 75 ohm resistor //C.0 is sound #pragma regalloc- //I allocate the registers myself #pragma optsize- //optimize for speed #include #include #include #include #include #include //cycles = 63.625 * 16 Note NTSC is 63.55 //but this line duration makes each frame exactly 1/60 sec //which is nice for keeping a realtime clock #define lineTime 1018 #define begin { #define end } #define ScreenTop 30 #define ScreenBot 230 #define T0reload 256-60 //State machine states #define intro1 0 #define intro2 1 #define intro3 2 #define intro4 3 #define introflash 4 #define choosetype1 5 #define choosetype2 6 #define choosetype3 7 #define choosetypeflash 8 #define choosedifficulty1 9 #define difficultyflash 10 #define waitstate 11 #define duckflying 12 #define duckflash 13 #define duckfalling 14 #define duckflyaway 15 #define roundover 16 #define results 17 #define gameover 18 //NOTE that v1 to v8 and i must be in registers! register char v1 @4; register char v2 @5; register char v3 @6; register char v4 @7; register char v5 @8; register char v6 @9; register char v7 @10; register char v8 @11; register int i @12; #pragma regalloc+ char syncON, syncOFF; int LineCount; int time; //animation variables char x, y, vx= 1, vy=1; //screen raster char screen[1600]; //counter variables char t, t2,ts[10]; //strings to be printed to the screen flash char welcome1[] = "WELCOME TO"; flash char welcome2[] = "DUCK HUNTER"; flash char welcome3[] = "CREATED BY"; flash char welcome4[] = "LEONARD BERGSMAN"; flash char welcome5[] = "MICHAEL PESSIKI "; flash char welcome7[] = "SHOOT THE SCREEN"; flash char welcome8[] = "TO BEGIN"; flash char select1[] = "SHOOT SCREEN"; flash char select2[] = "TO SELECT"; flash char select3[] = "SHOOT OFF SCREEN"; flash char select4[] = "TO SWITCH"; flash char select5[] = "ONE DUCK"; flash char select6[] = "TWO DUCKS"; flash char select7[] = "DIFFICULTY"; flash char select8[] = "EASY"; flash char select9[] = "MEDIUM"; flash char select10[] = "HARD"; flash char wait1[] = "GAME WILL BEGIN"; flash char wait2[] = "IN 4 SECONDS"; flash char getready[] = "GET READY"; flash char disp1[] = "DUCK"; flash char disp2[] = "HITS"; flash char disp3[] = "LVL"; flash char disp4[] = "SHOTS"; flash char result1[] = "LEVEL"; flash char result2[] = "COMPLETED"; flash char result3[] = "FAILED"; flash char result4[] = "SCORE"; flash char result6[] = "GAMEOVER"; flash char gameover1[] = "YOU GOT THE"; flash char gameover2[] = "HIGH SCORE"; char gamestate; char sensedlight=0; char pulllastcycle=0; char detectifhit=0; char triggerpull=0; char bird; char direction; char outputstring[20]; char outputstring2[20]; char delay; char detectflash; char numducks; char difficulty; char ducksremainround; char ducksremainlevel; char level; char hitslevel; char score; char shotsleft; char gameoverflag; char speed; char highscore=15; char counter; char delay2; char whichdirection; //tone related variables //set note to a number to play a tone char note, halfnote, notecount, inote, tnote; //define some musical notes modified to play the Cornell Alma Mater flash char notes[30]={45,40,36,40,45,54,54,60,40,45,48,45,40,40, 45,40,36,40,45,54,54,60,40,36,34,48,45,45}; //arrays for randomly choosing the starting direction flash char xdir[4] = {-1,1,-1,1}; flash char ydir[4] = {-1,-1,-1,-1}; //Point plot lookup table flash char pos[8]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01}; //define some character bitmaps //5x7 characters flash char bitmap[46][7]={ //0 0b01110000, 0b10001000, 0b10011000, 0b10101000, 0b11001000, 0b10001000, 0b01110000, //1 0b00100000, 0b01100000, 0b00100000, 0b00100000, 0b00100000, 0b00100000, 0b01110000, //2 0b01110000, 0b10001000, 0b00001000, 0b00010000, 0b00100000, 0b01000000, 0b11111000, //3 0b11111000, 0b00010000, 0b00100000, 0b00010000, 0b00001000, 0b10001000, 0b01110000, //4 0b00010000, 0b00110000, 0b01010000, 0b10010000, 0b11111000, 0b00010000, 0b00010000, //5 0b11111000, 0b10000000, 0b11110000, 0b00001000, 0b00001000, 0b10001000, 0b01110000, //6 0b01000000, 0b10000000, 0b10000000, 0b11110000, 0b10001000, 0b10001000, 0b01110000, //7 0b11111000, 0b00001000, 0b00010000, 0b00100000, 0b01000000, 0b10000000, 0b10000000, //8 0b01110000, 0b10001000, 0b10001000, 0b01110000, 0b10001000, 0b10001000, 0b01110000, //9 0b01110000, 0b10001000, 0b10001000, 0b01111000, 0b00001000, 0b00001000, 0b00010000, //A 0b01110000, 0b10001000, 0b10001000, 0b10001000, 0b11111000, 0b10001000, 0b10001000, //B 0b11110000, 0b10001000, 0b10001000, 0b11110000, 0b10001000, 0b10001000, 0b11110000, //C 0b01110000, 0b10001000, 0b10000000, 0b10000000, 0b10000000, 0b10001000, 0b01110000, //D 0b11110000, 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b11110000, //E 0b11111000, 0b10000000, 0b10000000, 0b11111000, 0b10000000, 0b10000000, 0b11111000, //F 0b11111000, 0b10000000, 0b10000000, 0b11111000, 0b10000000, 0b10000000, 0b10000000, //G 0b01110000, 0b10001000, 0b10000000, 0b10011000, 0b10001000, 0b10001000, 0b01110000, //H 0b10001000, 0b10001000, 0b10001000, 0b11111000, 0b10001000, 0b10001000, 0b10001000, //I 0b01110000, 0b00100000, 0b00100000, 0b00100000, 0b00100000, 0b00100000, 0b01110000, //J 0b00111000, 0b00010000, 0b00010000, 0b00010000, 0b00010000, 0b10010000, 0b01100000, //K 0b10001000, 0b10010000, 0b10100000, 0b11000000, 0b10100000, 0b10010000, 0b10001000, //L 0b10000000, 0b10000000, 0b10000000, 0b10000000, 0b10000000, 0b10000000, 0b11111000, //M 0b10001000, 0b11011000, 0b10101000, 0b10101000, 0b10001000, 0b10001000, 0b10001000, //N 0b10001000, 0b10001000, 0b11001000, 0b10101000, 0b10011000, 0b10001000, 0b10001000, //O 0b01110000, 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b01110000, //P 0b11110000, 0b10001000, 0b10001000, 0b11110000, 0b10000000, 0b10000000, 0b10000000, //Q 0b01110000, 0b10001000, 0b10001000, 0b10001000, 0b10101000, 0b10010000, 0b01101000, //R 0b11110000, 0b10001000, 0b10001000, 0b11110000, 0b10100000, 0b10010000, 0b10001000, //S 0b01111000, 0b10000000, 0b10000000, 0b01110000, 0b00001000, 0b00001000, 0b11110000, //T 0b11111000, 0b00100000, 0b00100000, 0b00100000, 0b00100000, 0b00100000, 0b00100000, //U 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b01110000, //V 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b01010000, 0b00100000, //W 0b10001000, 0b10001000, 0b10001000, 0b10101000, 0b10101000, 0b10101000, 0b01010000, //X 0b10001001, 0b10001001, 0b01010001, 0b00100001, 0b01010001, 0b10001001, 0b10001001, //Y 0b10001000, 0b10001000, 0b10001000, 0b01010000, 0b00100000, 0b00100000, 0b00100000, //Z 0b11111000, 0b00001000, 0b00010000, 0b00100000, 0b01000000, 0b10000000, 0b11111000, //figure1 0b01110000, 0b00100000, 0b01110000, 0b10101000, 0b00100000, 0b01010000, 0b10001000, //figure2 0b01110000, 0b10101000, 0b01110000, 0b00100000, 0b00100000, 0b01010000, 0b10001000, //Duckright1 0b00000000, 0b00001000, 0b00011010, 0b11111111, 0b01111000, 0b00100000, 0b00000000, //Duckright2 0b00000000, 0b00000000, 0b00000010, 0b11111111, 0b01111000, 0b00111000, 0b00001000, //Blank 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, //duckleft1 0b00000000, 0b00010000, 0b01011000, 0b11111111, 0b00011110, 0b00000100, 0b00000000, //duckleft2 0b00000000, 0b00000000, 0b01000000, 0b11111111, 0b00011110, 0b00011100, 0b00010000, //deadduck 0b00010000, 0b00111000, 0b11111110, 0b01111100, 0b00010000, 0b00011000, 0b00010000, //flyawayduck 0b00010000, 0b00011000, 0b00010000, 0b01111100, 0b11111110, 0b00111000, 0b00010000, //easytarget 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111 }; //================================ //3x5 font numbers, then letters //packed two per definition for fast //copy to the screen at x-position divisible by 4 flash char smallbitmap[43][5]={ //0 0b11101110, 0b10101010, 0b10101010, 0b10101010, 0b11101110, //1 0b01000100, 0b11001100, 0b01000100, 0b01000100, 0b11101110, //2 0b11101110, 0b00100010, 0b11101110, 0b10001000, 0b11101110, //3 0b11101110, 0b00100010, 0b11101110, 0b00100010, 0b11101110, //4 0b10101010, 0b10101010, 0b11101110, 0b00100010, 0b00100010, //5 0b11101110, 0b10001000, 0b11101110, 0b00100010, 0b11101110, //6 0b11001100, 0b10001000, 0b11101110, 0b10101010, 0b11101110, //7 0b11101110, 0b00100010, 0b01000100, 0b10001000, 0b10001000, //8 0b11101110, 0b10101010, 0b11101110, 0b10101010, 0b11101110, //9 0b11101110, 0b10101010, 0b11101110, 0b00100010, 0b01100110, //: 0b00000000, 0b01000100, 0b00000000, 0b01000100, 0b00000000, //= 0b00000000, 0b11101110, 0b00000000, 0b11101110, 0b00000000, //blank 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, //A 0b11101110, 0b10101010, 0b11101110, 0b10101010, 0b10101010, //B 0b11001100, 0b10101010, 0b11101110, 0b10101010, 0b11001100, //C 0b11101110, 0b10001000, 0b10001000, 0b10001000, 0b11101110, //D 0b11001100, 0b10101010, 0b10101010, 0b10101010, 0b11001100, //E 0b11101110, 0b10001000, 0b11101110, 0b10001000, 0b11101110, //F 0b11101110, 0b10001000, 0b11101110, 0b10001000, 0b10001000, //G 0b11101110, 0b10001000, 0b10001000, 0b10101010, 0b11101110, //H 0b10101010, 0b10101010, 0b11101110, 0b10101010, 0b10101010, //I 0b11101110, 0b01000100, 0b01000100, 0b01000100, 0b11101110, //J 0b00100010, 0b00100010, 0b00100010, 0b10101010, 0b11101110, //K 0b10001000, 0b10101010, 0b11001100, 0b11001100, 0b10101010, //L 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b11101110, //M 0b10101010, 0b11101110, 0b11101110, 0b10101010, 0b10101010, //N 0b00000000, 0b11001100, 0b10101010, 0b10101010, 0b10101010, //O 0b01000100, 0b10101010, 0b10101010, 0b10101010, 0b01000100, //P 0b11101110, 0b10101010, 0b11101110, 0b10001000, 0b10001000, //Q 0b01000100, 0b10101010, 0b10101010, 0b11101110, 0b01100110, //R 0b11101110, 0b10101010, 0b11001100, 0b11101110, 0b10101010, //S 0b11101110, 0b10001000, 0b11101110, 0b00100010, 0b11101110, //T 0b11101110, 0b01000100, 0b01000100, 0b01000100, 0b01000100, //U 0b10101010, 0b10101010, 0b10101010, 0b10101010, 0b11101110, //V 0b10101010, 0b10101010, 0b10101010, 0b10101010, 0b01000100, //W 0b10101010, 0b10101010, 0b11101110, 0b11101110, 0b10101010, //X 0b00000000, 0b10101010, 0b01000100, 0b01000100, 0b10101010, //Y 0b10101010, 0b10101010, 0b01000100, 0b01000100, 0b01000100, //Z 0b11101110, 0b00100010, 0b01000100, 0b10001000, 0b11101110, //space 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, //box 0b00000000, 0b01100110, 0b01100110, 0b00000000, 0b00000000, //: 0b00000000, 0b01000100, 0b00000000, 0b01000100, 0b00000000, //bullet 0b01000100, 0b11101110, 0b11101110, 0b11101110, 0b11101110 }; //================================== //This is the sync generator and raster generator. It MUST be entered from //sleep mode to get accurate timing of the sync pulses #pragma warn- interrupt [TIM1_COMPA] void t1_cmpA(void) begin //start the Horizontal sync pulse PORTD = syncON; //count timer 0 at 1/usec TCNT0=0; //update the curent scanline number LineCount ++ ; //begin inverted (Vertical) synch after line 247 if (LineCount==248) begin syncON = 0b00100000; syncOFF = 0; end //back to regular sync after line 250 if (LineCount==251) begin syncON = 0; syncOFF = 0b00100000; end //start new frame after line 262 if (LineCount==263) begin LineCount = 1; end delay_us(2); //adjust to make 5 us pulses //end sync pulse PORTD = syncOFF; if (LineCount=ScreenTop) begin //compute byte index for beginning of the next line //left-shift 4 would be individual lines // <<3 means line-double the pixels //The 0xfff8 truncates the odd line bit //i=(LineCount-ScreenTop)<<3 & 0xfff8; // #asm push r16 lds r12, _LineCount lds r13, _Linecount+1 ldi r16, 30 sub r12, r16 ldi r16,0 sbc r13, r16 lsl r12 rol r13 lsl r12 rol r13 lsl r12 rol r13 mov r16,r12 andi r16,0xf0 mov r12,r16 pop r16 #endasm //load 16 registers with screen info #asm push r14 push r15 push r16 push r17 push r18 push r19 push r26 push r27 ldi r26,low(_screen) ;base address of screen ldi r27,high(_screen) add r26,r12 ;offset into screen (add i) adc r27,r13 ld r4,x+ ;load 16 registers and inc pointer ld r5,x+ ld r6,x+ ld r7,x+ ld r8,x+ ld r9,x+ ld r10,x+ ld r11,x+ ld r12,x+ ld r13,x+ ld r14,x+ ld r15,x+ ld r16,x+ ld r17,x+ ld r18,x+ ld r19,x pop r27 pop r26 #endasm delay_us(3); //adjust to center image on screen //blast 16 bytes to the screen #asm ;but first a macro to make the code shorter ;the macro takes a register number as a parameter ;and dumps its bits serially to portD.6 ;the nop can be eliminated to make the display narrower .macro videobits ;regnum BST @0,7 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,6 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,5 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,4 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,3 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,2 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,1 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,0 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 .endm videobits r4 ;video line -- byte 1 videobits r5 ;byte 2 videobits r6 ;byte 3 videobits r7 ;byte 4 videobits r8 ;byte 5 videobits r9 ;byte 6 videobits r10 ;byte 7 videobits r11 ;byte 8 videobits r12 ;byte 9 videobits r13 ;byte 10 videobits r14 ;byte 11 videobits r15 ;byte 12 videobits r16 ;byte 13 videobits r17 ;byte 14 videobits r18 ;byte 15 videobits r19 ;byte 16 clt ;clear video after the last pixel on the line IN R30,0x12 BLD R30,6 OUT 0x12,R30 pop r19 pop r18 pop r17 pop r16 pop r15 pop r14 #endasm //check for light on screen if above bottom display if(LineCount>3) + ((int)y<<4) ; the byte with the pixel in it push r16 ldd r30,y+2 ;get x lsr r30 lsr r30 lsr r30 ;divide x by 8 ldd r12,y+1 ;get y lsl r12 ;mult y by 16 clr r13 lsl r12 rol r13 lsl r12 rol r13 lsl r12 rol r13 add r12, r30 ;add in x/8 ;v2 = screen[i]; r5 ;v3 = pos[x & 7]; r6 ;v4 = c r7 ldi r30,low(_screen) ldi r31,high(_screen) add r30, r12 adc r31, r13 ld r5,Z ;get screen byte ldd r26,y+2 ;get x ldi r27,0 andi r26,0x07 ;form x & 7 ldi r30,low(_pos*2) ldi r31,high(_pos*2) add r30,r26 adc r31,r27 lpm r6,Z ld r16,y ;get c ;if (v4==1) screen[i] = v2 | v3 ; ;if (v4==0) screen[i] = v2 & ~v3; ;if (v4==2) screen[i] = v2 ^ v3 ; cpi r16,1 brne tst0 or r5,r6 tst0: cpi r16,0 brne tst2 com r6 and r5,r6 tst2: cpi r16,2 brne writescrn eor r5,r6 writescrn: ldi r30,low(_screen) ldi r31,high(_screen) add r30, r12 adc r31, r13 st Z, r5 ;write the byte back to the screen pop r16 #endasm end #pragma warn+ //================================== // put a big character on the screen modified from original to print all 8 bits // c is index into bitmap void video_putchar(char x, char y, char c) begin v7 = x; for (v6=0;v6<7;v6++) begin v1 = bitmap[c][v6]; v8 = y+v6; video_pt(v7, v8, (v1 & 0x80)==0x80); video_pt(v7+1, v8, (v1 & 0x40)==0x40); video_pt(v7+2, v8, (v1 & 0x20)==0x20); video_pt(v7+3, v8, (v1 & 0x10)==0x10); video_pt(v7+4, v8, (v1 & 0x08)==0x08); video_pt(v7+5, v8, (v1 & 0x04)==0x04); video_pt(v7+6, v8, (v1 & 0x02)==0x02); video_pt(v7+7, v8, (v1 & 0x01)==0x01); end end //================================== // put a big character on the screen returned to normal // c is index into bitmap void video_putchar2(char x, char y, char c) begin v7 = x; for (v6=0;v6<7;v6++) begin v1 = bitmap[c][v6]; v8 = y+v6; video_pt(v7, v8, (v1 & 0x80)==0x80); video_pt(v7+1, v8, (v1 & 0x40)==0x40); video_pt(v7+2, v8, (v1 & 0x20)==0x20); video_pt(v7+3, v8, (v1 & 0x10)==0x10); video_pt(v7+4, v8, (v1 & 0x08)==0x08); // video_pt(v7+5, v8, (v1 & 0x04)==0x04); // video_pt(v7+6, v8, (v1 & 0x02)==0x02); // video_pt(v7+7, v8, (v1 & 0x01)==0x01); end end //================================== // put a string of big characters on the screen void video_puts(char x, char y, char *str) begin char i ; for (i=0; str[i]!=0; i++) begin if (str[i]>=0x30 && str[i]<=0x3a) video_putchar(x,y,str[i]-0x30); else video_putchar(x,y,str[i]-0x40+9); x = x+6; end end //================================== // put a small character on the screen // x-cood must be on divisible by 4 // c is index into bitmap void video_smallchar(char x, char y, char c) begin char mask; i=((int)x>>3) + ((int)y<<4) ; if (x == (x & 0xf8)) mask = 0x0f; //f8 else mask = 0xf0; screen[i] = (screen[i] & mask) | (smallbitmap[c][0] & ~mask); screen[i+16] = (screen[i+16] & mask) | (smallbitmap[c][1] & ~mask); screen[i+32] = (screen[i+32] & mask) | (smallbitmap[c][2] & ~mask); screen[i+48] = (screen[i+48] & mask) | (smallbitmap[c][3] & ~mask); screen[i+64] = (screen[i+64] & mask) | (smallbitmap[c][4] & ~mask); end //================================== // put a string of small characters on the screen // x-cood must be on divisible by 4 void video_putsmalls(char x, char y, char *str) begin char i ; for (i=0; str[i]!=0; i++) begin if (str[i]==32) video_smallchar(x,y,39); else if (str[i]>=0x30 && str[i]<=0x3a) video_smallchar(x,y,str[i]-0x30); else video_smallchar(x,y,str[i]-0x40+12); x = x+4; end end //================================== //plot a line //at x1,y1 to x2,y2 with color 1=white 0=black 2=invert //NOTE: this function requires signed chars //Code is from David Rodgers, //"Procedural Elements of Computer Graphics",1985 void video_line(char x1, char y1, char x2, char y2, char c) begin int e; signed char dx,dy,j, temp; signed char s1,s2, xchange; signed char x,y; x = x1; y = y1; dx = cabs(x2-x1); dy = cabs(y2-y1); s1 = csign(x2-x1); s2 = csign(y2-y1); xchange = 0; if (dy>dx) begin temp = dx; dx = dy; dy = temp; xchange = 1; end e = ((int)dy<<1) - dx; for (j=0; j<=dx; j++) begin video_pt(x,y,c) ; if (e>=0) begin if (xchange==1) x = x + s1; else y = y + s2; e = e - ((int)dx<<1); end if (xchange==1) y = y + s2; else x = x + s1; e = e + ((int)dy<<1); end end //================================== //return the value of one point //at x,y with color 1=white 0=black 2=invert char video_set(char x, char y) begin //The following construction //detects exactly one bit at the x,y location i=((int)x>>3) + ((int)y<<4) ; return ( screen[i] & 1<<(7-(x & 0x7))); end //================================== //clear the screen void video_clear(){ for(i=0 ; i<=1600 ; i++) screen[i] = 0; } //================================== //fill the screen void fill_screen(){ for(i=0 ; i<=1600 ; i++) screen[i] = 0b11111111; } //================================== // set up the ports and timers void main(void) begin //init timer 1 to generate sync OCR1A = lineTime; //One NTSC line TCCR1B = 9; //full speed; clear-on-match TCCR1A = 0x00; //turn off pwm and oc lines TIMSK = 0x10; //enable interrupt T1 cmp //init ports DDRD = 0xf0; //video out and switches //D.5 is sync:1000 ohm + diode to 75 ohm resistor //D.6 is video:330 ohm + diode to 75 ohm resistor DDRA = 0x00; PORTA = 0x01; DDRC = 0xff; //set port B as output for debugging //DDRB = 0xff; //PORTB = 0xff; //init timer 0 to 1/uSec TCCR0 = 2; //init the tones for audio // srand(1); note = 0; halfnote = note>>1; //initialize synch constants LineCount = 1; syncON = 0b00000000; syncOFF = 0b00100000; //init software timer t=0; time=0; //init animation x = 24; y=10; vx=1; vy=1; // video_pt(x,y,1); //enable sleep mode MCUCR = 0b10000000; #asm ("sei"); //The following loop executes once/video line during lines //1-230, then does all of the frame-end processing while(1) begin //stall here until next line starts //sleep enable; mode=idle //use sleep to make entry into sync ISR uniform time #asm ("sleep"); //The following code executes during the vertical blanking //Code here can be as long as //a total of 60 lines x 63.5 uSec/line x 8 cycles/uSec if (LineCount==231) { //calculations for determining state of gun triggerpull = PINA.0==0; //trigger is being held down if (triggerpull) pulllastcycle =1; //keep track of last state of trigger if (detectifhit) //if trigger was released { detectflash = 1; //set flag to check if hit detectifhit = 0; //make sure flag is unset next cycle } if (pulllastcycle && !triggerpull) { detectifhit = 1; //flag to flash screen if necessary pulllastcycle= 0; //reset trigger state } //state machine that controls the game switch(gamestate) { //first welcome screen case intro1: level=1; score=0; strcpyf(outputstring, welcome1); video_putsmalls(48,20,outputstring); strcpyf(outputstring, welcome2); //makes letters appear one at a time with a short delay if (delay++ == 30) { if (counter<11) { if (counter ==4) counter++; video_putchar(24+(8*counter),40,outputstring[counter]-55); counter++; } delay=0; delay2++; } //delay before going to the next state if (delay2==16){ gamestate= intro2 ; counter=0; } break; //second welcome screen case intro2: video_clear(); strcpyf(outputstring, welcome3); video_putsmalls(36,25,outputstring); delay = delay + 1; //one second delay if (delay == 60) { gamestate = intro3; delay = 0; delay2=0; } break; //displays creators names case intro3: strcpyf(outputstring, welcome4); strcpyf(outputstring2, welcome5); //prints out the creators names one letter at a time, with the first starting //at the first letter and the second starting at the last letter if (delay++ == 5) { if (counter<16) { if (counter ==7) counter++; video_putchar(4+(7*counter),40,outputstring[counter]-55); if (counter <15) video_putchar2(103-(7*counter),60,outputstring2[14-counter]-55); counter++; } delay=0; delay2++; } //another delay if (delay2==25) { gamestate= intro4; delay = 0; video_clear(); } break; //display before starting the menus case intro4: strcpyf(outputstring, welcome7); video_putsmalls(32,40,outputstring); strcpyf(outputstring, welcome8); video_putsmalls(40,60,outputstring); if(detectifhit){ gamestate = introflash; fill_screen(); } break; //flash the screen so that it can all be targeted case introflash: video_clear(); if(detectflash && sensedlight) gamestate = choosetype1; else gamestate = intro4; break; //display game instructions case choosetype1: video_clear(); strcpyf(outputstring, select1); video_putsmalls(24,20,outputstring); strcpyf(outputstring, select2); video_putsmalls(32,30,outputstring); delay++; //another delay if (delay == 120) { gamestate = choosetype2; delay = 0; } break; //display rest of instructions case choosetype2: strcpyf(outputstring, select3); video_putsmalls(24,50,outputstring); strcpyf(outputstring, select4); video_putsmalls(32,60,outputstring); delay++; //one more delay if (delay == 180) { gamestate = choosetype3; delay = 0; } break; //screen to allow the user to choose between one and two ducks case choosetype3: video_clear(); //display choices strcpyf(outputstring, select5); video_putsmalls(44,40,outputstring); strcpyf(outputstring, select6); video_putsmalls(44,50,outputstring); //display box indicating current selection if(numducks == 0) { video_smallchar(20,50,39); video_smallchar(20,40,40); } else if (numducks ==1) { video_smallchar(20,40,39); video_smallchar(20,50,40); } if(detectifhit){ gamestate = choosetypeflash; fill_screen(); } break; //flash screen to check whether the user shot at the screen case choosetypeflash: //if hit choose difficulty if (sensedlight) { gamestate = choosedifficulty1; } //if miss toggle choice if(!sensedlight) { gamestate = choosetype3; if (numducks == 0) numducks =1; else numducks = 0; } break; //screen to allow user to choose difficulty case choosedifficulty1: video_clear(); //display choices strcpyf(outputstring, select7); video_putsmalls(44,10,outputstring); strcpyf(outputstring, select8); video_putsmalls(44,30,outputstring); strcpyf(outputstring, select9); video_putsmalls(44,50,outputstring); strcpyf(outputstring, select10); video_putsmalls(44,70,outputstring); //display selection box if(difficulty == 0) { video_smallchar(20,50,39); video_smallchar(20,70,39); video_smallchar(20,30,40); } if (difficulty == 1) { video_smallchar(20,30,39); video_smallchar(20,70,39); video_smallchar(20,50,40); } if (difficulty == 2) { video_smallchar(20,30,39); video_smallchar(20,50,39); video_smallchar(20,70,40); } if(detectifhit){ gamestate = difficultyflash; fill_screen(); } break; case difficultyflash: //if miss select next option if(!sensedlight) { gamestate = choosedifficulty1; difficulty++; if (difficulty==3) difficulty=0; } //if hit determine speed and go on to continue setting up the game else { gamestate = waitstate; if (difficulty==0 || difficulty==1) speed=1; else speed=2; vx=speed; vy=speed; } break; //brief pause before each level and setup case waitstate: video_clear(); //display get ready strcpyf(outputstring, getready); video_putsmalls(44,40,outputstring); //setup variables that are the same for each level ducksremainlevel=10; time=0; hitslevel=0; shotsleft=3; ducksremainround=numducks+1; //setup initial speed and direction if (difficulty==0 || difficulty==1) speed=1; else speed =2; whichdirection=floor(rand()/8192); vx=speed*xdir[whichdirection]; vy=speed*ydir[whichdirection]; //one more delay delay++; if (delay == 180) { video_clear(); //draw displays at bottom of screen if (shotsleft==3){ video_smallchar(116,93,42); video_smallchar(120,93,42); video_smallchar(124,93,42); } strcpyf(outputstring, disp1); video_putsmalls(0,93,outputstring); video_smallchar(16,93,41); strcpyf(outputstring, disp2); video_putsmalls(32,93,outputstring); video_smallchar(44,93,41); strcpyf(outputstring, disp3); video_putsmalls(64,93,outputstring); video_smallchar(76,93,41); strcpyf(outputstring, disp4); video_putsmalls(92,93,outputstring); video_smallchar(112,93,41); sprintf(ts,"%02d",level); video_putsmalls(80,93,ts); gamestate = duckflying; delay = 0; } break; //duck flys on screen and you shoot at it (actual game play) case duckflying: video_putchar(x,y,40) ; //erase old one //move the duck x = x + vx; y = y + vy; //quarter second time base for flapping wings if (++t2>14) { t2=0; time = time + 1; } //duck bounces off the walls of the screen if (x>=114) { vx = -speed; } else if (x<=3) { vx = speed; } if (y>=78) vy = -speed; else if (y<=3) vy = speed; //toggle which way the duck is facing if (vx==1 || vx==2) direction =38; else direction = 41; //determine which duck character to output bird = direction + time%2; //display bird video_putchar(x,y,bird); if (shotsleft>0){ if(detectifhit){ shotsleft--; //decrement shots remaining on trigger release gamestate = duckflash; //display the duck as a box flash if difficuly level is easy or hard if(difficulty==0 || difficulty ==2) video_putchar(x,y,45); } } //if you take too long, the duck flies away if(time == 17){ time = 0; gamestate = duckflyaway; } //update display sprintf(ts,"%02d",(11-ducksremainlevel)); video_putsmalls(20,93,ts); sprintf(ts,"%02d",hitslevel); video_putsmalls(48,93,ts); break; //state where we determine if duck was hit or not case duckflash: //if sensed light, count as a hit, goto duck falling if(sensedlight){ hitslevel++; score=score+(difficulty+1)*(numducks+1); gamestate = duckfalling; } //if miss, continue flying else gamestate=duckflying; //update display sprintf(ts,"%02d",(11-ducksremainlevel)); video_putsmalls(20,93,ts); sprintf(ts,"%02d",hitslevel); video_putsmalls(48,93,ts); //update bullets remaining display if (shotsleft==0) { video_smallchar(116,93,39); video_smallchar(120,93,39); video_smallchar(124,93,39); } if (shotsleft==1) { video_smallchar(116,93,42); video_smallchar(120,93,39); video_smallchar(124,93,39); } if (shotsleft==2){ video_smallchar(116,93,42); video_smallchar(120,93,42); video_smallchar(124,93,39); } if (shotsleft==3){ video_smallchar(116,93,42); video_smallchar(120,93,42); video_smallchar(124,93,42); } break; // dead duck falls to bottom of screen case duckfalling: video_putchar(x,y,40); //erase old duck //fall till hits bottom if (y<=78){ vy=1; y=y+vy; } else{ vy=-speed; ducksremainround--; video_putchar(x,y,40); //erase old falling duck if (ducksremainround==0) gamestate=roundover; else { x = 2 + floor(rand()/350); //new duck starts at random x coordinate at bottom of screen y = 72; gamestate = duckflying; } time=0; ducksremainlevel--; } video_putchar(x,y,43); //draw falling duck break; case duckflyaway: video_putchar(x,y,40); //erase old duck //fly away till duck reaches top of screen if (y>2){ vy=-1; y=y+vy; } else { time=0; video_putchar(x,y,40); ducksremainlevel--; ducksremainround--; if (ducksremainround==0){ gamestate = roundover; } else { vy=speed; x = 2 + floor(rand()/350); //new duck at random x coordinate y=72; gamestate = duckflying; } } video_putchar(x,y,44); //draw flying away duck break; //roundover resets shots and starts new round (of one duck or two) case roundover: video_putchar(x,y,40); //erase duck shotsleft =3; ducksremainround=numducks+1; //choose random starting direction whichdirection=floor(rand()/8192); vx=speed*xdir[whichdirection]; vy=speed*ydir[whichdirection]; //short delay b/w rounds if (time++ ==60){ x = 2 + floor(rand()/350); y=72; time =0; if (ducksremainlevel==0) gamestate = results; //level over else gamestate = duckflying; //new round } //redraws 3 bullets video_smallchar(116,93,42); video_smallchar(120,93,42); video_smallchar(124,93,42); break; //display score and results between levels case results: video_clear(); //determine whether the user shot enough ducks to continue switch (level){ case 1: if (hitslevel <6) gameoverflag=1; break; case 2: if (hitslevel <6) gameoverflag=1; break; case 3: if (hitslevel <7) gameoverflag=1; break; case 4: if (hitslevel <7) gameoverflag=1; break; case 5: if (hitslevel <8) gameoverflag=1; break; case 6: if (hitslevel <8) gameoverflag=1; break; case 7: if (hitslevel <9) gameoverflag=1; break; case 8: if (hitslevel <9) gameoverflag=1; break; default: if (hitslevel <10) gameoverflag=1; } //displays end of level information strcpyf(outputstring, result1); video_putsmalls(20,20,outputstring); sprintf(ts,"%02d",level); video_putsmalls(44,20,ts); strcpyf(outputstring, result4); video_putsmalls(40,40,outputstring); //displays score sprintf(ts,"%03d",score); video_putsmalls(64,40,ts); //displays Level # Complete or Failed based on gameover flag if (!gameoverflag){ strcpyf(outputstring, result2); video_putsmalls(56,20,outputstring); } else { strcpyf(outputstring, result3); video_putsmalls(56,20,outputstring); strcpyf(outputstring, result6); video_putsmalls(40,60,outputstring); } //leave info on screen for a few seconds if (time++ ==200){ time=0; if (!gameoverflag) { //setup next level level= level + 1; gamestate = waitstate; } else { video_clear(); gamestate=gameover; } } break; //gameover shows high score and restarts the game case gameover: video_clear(); //see if your score beat high score if (score>=highscore){ highscore=score; strcpyf(outputstring, gameover1); video_putsmalls(32,40,outputstring); } strcpyf(outputstring, gameover2); video_putsmalls(32,60,outputstring); sprintf(ts,"%d",highscore); video_putsmalls(80,60,ts); gameoverflag = 0; //leave high score info on screen for a few seconds, then reset if (time++==255){ time=0; gamestate = 0; video_clear(); delay=0; delay2=0; counter=0; inote=0; } break; }//end of gamestate switch //play alma mater if in beginning menus if (gamestate<=10){ //tnote is the duration of each note in 1/60 second units if (tnote++ == 30) begin tnote = 0; //update tone //inote is index into notes if (inote++==29) inote=0; note = notes[inote]; halfnote = note>>1; end } //play falling and flyaway sound while in game play else { if (gamestate==duckflyaway) note = 32+4*(y%2); //creates flapping wing sound on flyaway else if (gamestate==duckfalling) note=y; //creates a falling sound based on y coordinate of falling duck else note = 3; halfnote = note>>1; } triggerpull=0; sensedlight=0; detectflash = 0; } //line 231 } //while } //main