/* COPYRIGHT Edward Cree, 2010 LICENSED UNDER GNU GPL v3+ */ #include #include #include void ccls(char i, char p, char b); void cprint(char * string, char i, char p, char b); void catprint(char * string, char i, char p, char b, char x, char y); void atprint(char * string, char x, char y); int mainmenu(void); void abend(void); void maingame(void); void printscore(int score); void game_over(int score); #define min(a,b) ((a)<(b)?(a):(b)) #define max(a,b) ((a)>(b)?(a):(b)) extern unsigned char settings; extern char *ntrack1, *ntrack2, *ntrack3; int main(void) { int state=0; zx_border(WHITE); settings=1; menu: putchar(1); putchar(64); // narrow chars mode ccls(0, 7, 0); catprint("\n", 6, 1, 0, 23, 0); while(getk()); state=mainmenu(); switch(state) { case 0: // Game maingame(); break; case 1: // Settings showset: ccls(0, 7, 0); catprint("Settings\n", 6, 1, 0, 27, 0); cprint((settings&0x03)?"\n[m] Music ON\n":"\n[m] Music OFF\n", 1, 7, 0); if(settings&0x03) { char *tn=""; char msg[32]; switch(settings&0x03) { case 1: tn=&ntrack1; break; case 2: tn=&ntrack2; break; case 3: tn=&ntrack3; break; } sprintf(msg, "[t] Track 0%u (%s)\n", settings&0x03, tn); cprint(msg, 2, 7, 0); } else { cprint("[t] Track --\n", 0, 7, 0); } cprint("[space] Back to main menu\n", 1, 7, 1); while(1) { char c=getk(); int t=settings&0x03; switch(c) { case ' ': goto menu; break; case 'm': settings=(settings&0xFC)|(t?0:1); goto showset; break; case 't': if(t) { t=(t%3)+1; settings=(settings&0xFC)|(t&0x03); // shouldn't really need to & with 0x03, but, just to be safe... } goto showset; break; } } break; case 2: // Help ccls(0, 7, 0); catprint("Advanced Body Disposal Simulator\n", 6, 1, 0, 15, 0); cprint("\nHelp:\n", 1, 7, 0); cprint("You are a low-ranking mook of the Czechistan Mafia, tasked with\n", 0, 7, 0); cprint("disposing of the bodies of those citizens your masters consider\n", 0, 7, 0); cprint("to be \"undesirables\". Your dropoff points are seven dumpsters\n", 0, 7, 0); cprint("located around Czechia City. (These appear on the map in ", 0, 7, 0); cprint("green", 0, 4, 0); cprint(")\n", 0, 7, 0); cprint("For 'safety' reasons, the mafiosi will not allow you to place\n", 0, 7, 0); cprint("another body into a dumpster until its previous occupant has\n", 0, 7, 0); cprint("decomposed. Full dumpsters appear on the map in ", 0, 7, 0); cprint("red", 0, 2, 0); cprint(".\n", 0, 7, 0); cprint("The Czechia City police force are not in the mafia's pocket yet\n", 0, 7, 0); cprint("so beware of officers roaming the city on their beat. If you\n", 0, 7, 0); cprint("are caught with a dead body slung under your arm, no lawyer in\n", 0, 7, 0); cprint("the whole of Czechistan will be able to keep you out of jail.\n\n", 0, 7, 0); cprint("Controls are HJKL (i.e. vi keys), because that way I only have\n", 0, 7, 0); cprint("to read one half-row ;-)\n", 0, 7, 0); cprint("\nPress any key\n", 4, 7, 1); while(getk()); while(!getk()); goto menu; break; case 3: // Exit ccls(7, 0, 0); catprint("Thankyou for playing A.B.D.S.\n", 6, 1, 0, 19, 0); while(getk()); while(!getk()); return(0); break; default: abend(); // essentially a panic return(1); break; } } void ccls(char i, char p, char b) { putchar(16); // set ink colour putchar('0'+i); putchar(17); // set paper colour putchar('0'+p); putchar(19); // set bright putchar(b?'1':'0'); putchar(12); // CLS (form feed) } void cprint(char * string, char i, char p, char b) { putchar(16); // set ink colour putchar('0'+i); putchar(17); // set paper colour putchar('0'+p); putchar(19); // set bright putchar(b?'1':'0'); printf("%s", string); } void catprint(char * string, char i, char p, char b, char x, char y) { putchar(22); // set location putchar(32+y); putchar(32+x); putchar(16); // set ink colour putchar('0'+i); putchar(17); // set paper colour putchar('0'+p); putchar(19); // set bright putchar(b?'1':'0'); printf("%s", string); } void atprint(char * string, char x, char y) { putchar(22); // set location putchar(32+y); putchar(32+x); printf("%s", string); } int mainmenu(void) { int menusel=0; char *menuitem[4]; int i; int e=0; menuitem[0]=" New game "; menuitem[1]=" Settings "; menuitem[2]=" Help "; menuitem[3]=" Exit "; catprint(" Main menu ", 1, 6, 1, 26, 8); catprint(" ", 7, 0, 0, 26, 9); while(e==0) { for(i=0;i<4;i++) { catprint(menuitem[i], 0, i==menusel?5:7, 0, 26, 10+i); } i=getk(); switch(i) { case 11: // up menusel=(menusel+3)%4; break; case 10: // down menusel=(menusel+1)%4; break; case 13: // enter/return (select) e=1; break; } } return(menusel); } void abend(void) { ccls(2, 0, 1); atprint("SYSTEM ERROR. RETURNED TO BASIC", 1, 1); atprint("", 1, 20); } void maingame(void) { #asm ;// set up interrupt routine di ld a,(_settings) and 0x03 jr z,noi dec a ld hl,tracklist ;// find the track from the tracklist vector table ld d,0 ld e,a add hl,de add hl,de ld c,(hl) inc hl ld b,(hl) ld (curtrack),bc ;// set up IM2 vector table ld hl, 0xc000 ld (hl),0xdd ld bc, 0x100 push hl pop de inc de ldir ;// install interrupt handler scf ccf ld hl,endihandler ld de,ihandler sbc hl,de push hl pop bc ld hl,ihandler ld de,0xdddd ldir ;// set interrupt vector & IM 2 ld a,0xc0 ld i,a im 2 ei .noi call drawmap ld a, 0 push af .loop call mus pop af ld ix,seqtbl inc a and 0x07 ld e,a ld d,0 push af add ix,de add ix,de ld l,(ix+0) ld h,(ix+1) jp (hl) .seqtbl defw drawplayer defw drawcop defw moveplayer defw movecop defw dumps defw pldump defw loop defw loop .movecop exx ld a,r ld b,a ld c,a add hl,bc push hl exx pop hl ld a,(hl) add b and 0x1f ld b,a and 0x1c jr nz,loop ld a,b and 0x03 jr z,pcleft dec a jr z,pcup dec a jr z,pcdown dec a jr z,pcright jr loop ;// shouldn't get here .pcleft ld bc,(pcx) ;// b is pcy, c is pcx dec c push bc jp pcmove .pcright ld bc,(pcx) ;// b is pcy, c is pcx inc c push bc jp pcmove .pcup ld bc,(pcx) ;// b is pcy, c is pcx dec b push bc jp pcmove .pcdown ld bc,(pcx) ;// b is pcy, c is pcx inc b push bc jp pcmove .pcmove srl c srl c srl c sla b sla b ld a,b or c ld c,a ld b,0 ld hl,mapdata add hl,bc pop bc push bc ld a,c and 0x07 ld b,a ld a,(hl) .pcmgetbit rlca djnz pcmgetbit and 0x80 pop bc jp z, loop push bc ld bc,(pcx) ld a,b and 0x07 rrca rrca rrca or c ld l,a ld a,b and 0x18 add 0x40 ld h,a ld b,8 xor a .clearpcline ld (hl),a inc h djnz clearpcline pop bc ld (pcx),bc ld hl,(plx) sbc hl,bc jp nz, loop jp gameover .moveplayer ;// keys HJKL. D4 is H. H is left, J is down, K is up, L is right (vikeys) ld bc,0xbffe in a,(c) ld b,a ld a,iyh and a jr nz, nokey ld a,b and 0x10 jr z, left ld a,b and 0x08 jr z, down ld a,b and 0x04 jr z, up ld a,b and 0x02 jr z, right jp loop .nokey ld a,b and 0x1e cp 0x1e jp nz,loop ld iyh,0 jp loop .left ld bc,(plx) ;// b is ply, c is plx dec c push bc jp plmove .right ld bc,(plx) ;// b is ply, c is plx inc c push bc jp plmove .up ld bc,(plx) ;// b is ply, c is plx dec b push bc jp plmove .down ld bc,(plx) ;// b is ply, c is plx inc b push bc jp plmove .plmove ld iyh,1 srl c srl c srl c sla b sla b ld a,b or c ld c,a ld b,0 ld hl,mapdata add hl,bc pop bc push bc ld a,c and 0x07 ld b,a ld a,(hl) .plmgetbit rlca djnz plmgetbit and 0x80 pop bc jp z, loop push bc ld bc,(plx) ld a,b and 0x07 rrca rrca rrca or c ld l,a ld a,b and 0x18 add 0x40 ld h,a ld b,8 xor a .clearplline ld (hl),a inc h djnz clearplline pop bc ld (plx),bc jp loop .drawplayer ld bc,(plx) ;// b is ply, c is plx ld a,b and 0x07 rrca rrca rrca or c ld l,a ld a,b and 0x18 add 0x40 ld h,a ld ix,plsprite ld b,8 .drawplline ld a,(ix+0) ld (hl),a inc ix inc h djnz drawplline jp loop .drawcop ld bc,(pcx) ;// b is pcy, c is pcx ld a,b and 0x07 rrca rrca rrca or c ld l,a ld a,b and 0x18 add 0x40 ld h,a ld ix,pcsprite ld b,8 .drawpcline ld a,(ix+0) ld (hl),a inc ix inc h djnz drawpcline jp loop .dumps ld a,(dumptimer) dec a ld (dumptimer),a jp nz,loop ld a,(ndumps) ld b,0 ld c,a push bc ld hl,dumpdata .eachdump ld d,(hl) inc hl ld e,(hl) inc hl ld a,(hl) and a jr z, dumpgreen dec a ld (hl),a ld a,0x10 jr drawdump .dumpgreen ld a,0x20 .drawdump inc hl push hl ld hl,0x5800 ld b,0 ld c,d add hl,bc ld c,e sla c rl b sla c rl b sla c rl b sla c rl b sla c rl b add hl,bc ld (hl),a pop hl pop bc inc b push bc ld a,b cp c jr nz, eachdump jp loop .pldump ld a,(ndumps) ld b,0 ld c,a ld hl,dumpdata .pleachdump push bc ld d,(hl) inc hl ld e,(hl) inc hl ld a,(hl) inc hl and a jr z, pldumpisgreen jr nextdump .pldumpisgreen ld bc,(plx) ;// b is ply, c is plx ld a,c cp d jr nz, nextdump ld a,b cp e jr nz, nextdump dec hl ld (hl),96 inc hl ld ix, score ld d,(ix+1) ld e,(ix+0) inc de ld (ix+0),e ld (ix+1),d push de call _printscore pop de .nextdump pop bc inc b ld a,b cp c jr nz, pleachdump jp loop .gameover pop af im 1 ld ix, score ld d,(ix+1) ld e,(ix+0) push de call _game_over jp 0 ;// soft reboot .score defw 0 .plx defb 1 .ply defb 1 .pcx defb 15 .pcy defb 10 .plsprite defb 0x06 defb 0x36 defb 0x3e defb 0x2c defb 0x6c defb 0xdc defb 0xd4 defb 0x14 .pcsprite defb 0x08 defb 0x1C defb 0x36 defb 0x3A defb 0x7A defb 0x7F defb 0x55 defb 0x7F .drawmap ;// draw the level map ld hl,0x4000 ld bc,0x17ff ld de,0x4001 ld (hl),0 ldir ld ix,mapdata ld de,0 ;// d is x, e is y .eightmap ld hl,0x5800 ld b,0 ld c,d add hl,bc ld c,e sla c rl b sla c rl b sla c rl b sla c rl b sla c rl b add hl,bc ld b,8 ld a,(ix+0) .oneget push af and 0x80 jr z,black ld c,0x38 jr onemap .black ld c,0x07 .onemap ld (hl),c pop af sla a inc d inc hl djnz oneget inc ix ld a,d and 0x1f jr nz, eightmap ld d,0 inc e ld a,e cp 20 ;// 20dec lines jr nz, eightmap ret .mus ;// iterate the music; uses a, de and hl ld a,(_settings) and 0x03 ret z ld a,(notepos) and a jr nz, noch ld a,(notestate) xor 0xff ld (notestate),a out (0xfe),a ld hl,trackpos ld e,(hl) inc hl ld d,(hl) srl d ;// divide the framecounter by 32 to get the byte offset rr e srl d rr e srl d rr e srl d rr e ld hl,(curtrack) add hl,de ld a,(hl) and 0x3f ld (notepos),a ret .noch ld hl,notepos dec (hl) ret .tracklist defw track1 defw track2 defw track3 ._ntrack1 defm "Doomed" defb 0 ._ntrack2 defm "Manic" defb 0 ._ntrack3 defm "Annoying" defb 0 .track1 // Notes: B=24 C=23 C#=22 D=21 D#=20 E=19 F=18 F#=17 G=16 G#=15 // these are horrifically out of tune. Which is good, because it's the CGC defb 24 ; defb 23 defb 22 defb 23 defb 21 defb 23 defb 20 defb 26 defb 24 ; defb 23 defb 22 defb 23 defb 21 defb 23 defb 20 defb 20 defb 24 ; defb 23 defb 22 defb 23 defb 21 defb 23 defb 20 defb 26 defb 24 ; defb 23 defb 22 defb 23 defb 21 defb 23 defb 20 defb 20 defb 21 ; defb 20 defb 19 defb 20 defb 18 defb 20 defb 17 defb 21 defb 19 ; defb 18 defb 17 defb 18 defb 16 defb 18 defb 15 defb 15 defb 0 .track2 defb 21 ; defb 19 defb 18 defb 16 defb 14 defb 18 defb 14 defb 14 defb 15 ; defb 19 defb 15 defb 15 defb 16 defb 20 defb 16 defb 16 defb 21 ; defb 19 defb 18 defb 16 defb 14 defb 18 defb 14 defb 10 defb 11 ; defb 14 defb 18 defb 14 defb 11 defb 11 defb 11 defb 11 defb 0 .track3 defb 2 defb 2 defb 2 defb 3 defb 4 defb 5 defb 4 defb 3 defb 0 .trackpos defw 0 ;// pointer within current music track (frames) .curtrack defw 0 ;// pointer to current music track .notepos defb 0 ;// note generation counter .notestate defb 0 ;// last byte written to 0xfe ._settings defb 1 ;// bits 0 and 1 are music track (0 = no music) .ihandler ;// interrupt handler code (is installed to 0xdddd) di push hl push de push af ld a,(_settings) and 0x03 jr z,do_reti ;// increment the trackframes counter ld hl,trackpos ld e,(hl) inc hl ld d,(hl) inc de ld (hl),d dec hl ld (hl),e ;// check for end-of-track srl d ;// divide the framecounter by 32 to get the byte offset rr e srl d rr e srl d rr e srl d rr e ld hl,(curtrack) add hl,de ld a,(hl) and a ;// see if it's the end of track (sentinel 0) jr nz, do_reti ld hl,trackpos ld (hl), 0 ;// loop the track inc hl ld (hl), 0 .do_reti pop af pop de pop hl ei ret .endihandler .mapdata ;// the level map defb 0x00 defb 0x00 defb 0x00 defb 0x00 defb 0x7F defb 0x1E defb 0x42 defb 0xEE defb 0x49 defb 0x13 defb 0x7E defb 0xAA defb 0x59 defb 0x11 defb 0x53 defb 0xAA defb 0x71 defb 0xF7 defb 0xD8 defb 0x3A defb 0x43 defb 0x1C defb 0x4B defb 0xEA defb 0x46 defb 0x14 defb 0x7A defb 0x4A defb 0x7C defb 0x14 defb 0x42 defb 0x42 defb 0x47 defb 0xF7 defb 0xDE defb 0x5E defb 0x41 defb 0x11 defb 0x14 defb 0x72 defb 0x41 defb 0x11 defb 0x74 defb 0x12 defb 0x41 defb 0x11 defb 0x44 defb 0x1E defb 0x41 defb 0xFF defb 0xF4 defb 0x74 defb 0x77 defb 0x41 defb 0x14 defb 0x26 defb 0x55 defb 0x41 defb 0xD7 defb 0xE2 defb 0x55 defb 0x40 defb 0x50 defb 0x22 defb 0x5D defb 0x7C defb 0x5F defb 0x3A defb 0x41 defb 0x44 defb 0x51 defb 0x2A defb 0x7F defb 0x47 defb 0xD7 defb 0xEE defb 0x00 defb 0x00 defb 0x00 defb 0x00 ;// end of level map data .ndumps defb 7 .dumptimer defb 0 .dumpdata defb 17 defb 1 defb 0 defb 25 defb 7 defb 0 defb 15 defb 10 defb 0 defb 25 defb 12 defb 0 defb 4 defb 16 defb 0 defb 9 defb 18 defb 0 defb 21 defb 18 defb 0 #endasm } void printscore(int score) { char msg[16]; sprintf(msg, "Score: %u", score); catprint(msg, 4, 1, 1, 0, 21); } void game_over(int score) { char msg[16]; sprintf(msg, "Score: %u", score); catprint(msg, 4, 1, 1, 0, 21); catprint("G-G-G-GAME OVER!!!", 2, 1, 1, 23, 10); #asm di ld b,0x20 .go2loop ld hl,0 .goloop ld a,l and 0x01 dec a out (0xfe),a dec hl or h jr nz, goloop djnz go2loop ei #endasm while(getk()); while(!getk()); }