PROGRAM Frogger; NOTE !!! Code needed to create sprites for this game are contained at the end !! Follow the instructions. {Frogger version 0.92, Copyright 1995-1996 Jonas Maebe (AKA Gamefreak)} {Send comments to Jonas Maebe, 2:292/624.7 (Fidonet), in the } {Fidonet International Pascal conference or email at JMaebe@dma.be } {Hereby I give this code to the freeware circuit, which implies that } {everyone can use this code in ANY program, as long as I receive the } {applicable credit(s). } {I am in NO way responsible for any kind of damage directly or } {indirectly caused by this program (just to be safe :) } {$g+,a+,r-,s-,i-,x-,n-,e-,f-,d-,l-,v-,b-} {$m 8000, 140000 ,140000} {NEVER enable range checking ($R+), because it'll crash the computer.} {I don't know why, but I guess it has something to do with the Move386} {procedure.} {$ifdef ver70} {$q-} {$endif} {*define p386} {replace the '*' by a '$' to speed up the code somewhat if you have a 386 instead of a 486 or up. Probably not noticable, but who cares? :)} {$define slow} {replace the '*' by a '$' to slow everything down, necessary when playing on a VLB/PCI videocard} {$define move32bit} {disable this one if you have a ISA video card or a 386SX, I've heard 16 bit moves are faster on those systems} {$define v_retrace} {disable this one if the animation is jerky, it will speed up the screen redraw and thus improve the animation. Drawback: some fuzz... Also, if the game runs smooth with it, it probably won't without it} {*define invincible} {Have a wiiiiiild guess :)} {*define idspispopd} {Remeber Doom? Of course you do! Ever cheated? Of course you did! :) BTW: pretty useless if not combined with the above one} {$define bothsides} {If this one is not defined, the clipped sprite aren't continued at the other side of the screen} TYPE position = RECORD x,y:WORD END; big_tile = ARRAY[0..15,0..15] of BYTE; small_tile = ARRAY[0..9,0..9] of BYTE; Virscreen = Array [1..64000] of byte; VirPtr = ^Virscreen; treetype = (left,middle,right); TturtleDepth = (up, med, down, under); CONST retrace = 3; treemid1 = 4; treemid2 = 2; noftree1 = 2; noftree2 = 3; car1y = 163*320; {since the y-coords of the cars and turtles} car2y = 147*320; {don't change, immeditely multiply them by} car3y = 131*320; {320} car4y = 115*320; SpriteXsize = 10; SpriteYsize = 10; {With these two constants you can change the spritesize used by the drawsprite procedures. WARNING: SpriteXsize HAS to be a multiple of two!!!} rightclip = 273 - SpriteXsize; leftclip = 1; {And these two can be modified to enlarge or reduce the playing field. You'll also need them when you plan on using the drawsprite procedures in your own programs.} turtley: ARRAY[1..3] of WORD = ((16*5+3)*320, (16*3+3)*320,(16+3)*320); waterinc: ARRAY[1..5] of INTEGER = (1,-1,2,1,-1); frogtop: ARRAY[1..6] of BOOLEAN = (false,false,false,false,false,false); keyb: word = 8; stop: boolean = false; lives: byte = 4; car1pos: ARRAY[0..2,0..2] of INTEGER = ((259,236,213),(170,147,123),(78,55,32)); car2pos: ARRAY[0..2,0..2] of INTEGER = ((259,236,213),(170,147,123),(78,55,32)); car3pos: ARRAY[0..2] of WORD = (262,173,84); car4pos: ARRAY[0..2,0..2] of INTEGER = ((259,236,213),(170,147,123),(78,55,32)); turtlepos: ARRAY[1..3,0..2,0..2] of INTEGER = (((257,244,231),(167,154,141),(73,60,47)), ((231,243,255),(141,153,165),(47,59,71)), ((231,243,255),(141,153,165),(47,59,71))); tree1pos: ARRAY[1..2,1..(treemid1+2)] of WORD = ((12,22,32,42,52,62),(150,160,170,180,190,200)); tree2pos: ARRAY[1..3,1..(treemid2+2)] of WORD = ((56,46,36,26),(147,137,127,117),(238,228,218,208)); TurtleDepth: TTurtleDepth = up; TurtleDepthCount: WORD = 0; cyclecount: BYTE = retrace; fpstime1 : LONGINT = 0; fpstime2: LONGINT = 0; frames: LONGINT = 0; CTurtleDepth: ARRAY[1..7] of TturtleDepth = (up, med, down, under, down, med, up); VAR frogpos: position; pall: ARRAY[0..255,0..2] OF BYTE; grass, water: big_tile; ch: CHAR; Virscr, background : VirPtr; Vaddr, backaddr : word; savedi, savecx: WORD; frog, car1, car2, car3, car4, turtle, turtle2{, skull}: small_tile; tree: ARRAY[treetype] of small_tile; {turtle dive vars} TurtleDo1Le, TurtleDo2Le: Small_tile; Time: WORD; {now some arrays which hold the offsets of the sprites} CONST treeofs: ARRAY[0..2] of WORD = (ofs(tree[left]),ofs(tree[middle]), ofs(tree[right])); TurtleOfs: ARRAY[1..3] OF WORD = (ofs(turtle), ofs(TurtleDo1Le), ofs(TurtleDo2Le)); FUNCTION keypressed: BOOLEAN; INLINE($b4/$01/$cd/$16/$b0/$00/$74/$02/$fe/$c0); {mov ah,1;int $16;mov al,0;jnz $+2;inc al} FUNCTION readkey: CHAR; INLINE($b4/$10/$cd/$16/$88/$e0); {mov ah,$10;int $16;mov al, ah} {Sound and nosound are asm-translations of the Pascal code found in PCGPE10} procedure Sound(frequency : word); inline ($ba/$12/$00/$b8/$dd/$34/ $59/ $f7/$f1/ $89/$c3/ $b0/$b6/ {mov dx, $12;mov ax, $34dd;pop cx; div cx; mov bx, ax;mov al, $b6} $e6/$43/ $88/$d8/ $e6/$42/ $88/$f8/ $e6/$42/ $e4/$61/ {out $43, al;mov al,bl;out $42,al;mov al, bh;out $42, al;in al, $61} $0c/$03/ $e6/$61); {or al, 3;out $61, al} procedure NoSound; INLINE($E4/$61/ $24/$FC/ $E6/$61); {in al, $61; and al, $fc; out $61, al} PROCEDURE lawn(y:word); {draw the lawn :)} VAR number,row,column:BYTE; BEGIN FOR number := 0 to 16 DO FOR row := 0 to 15 DO FOR column := 0 to 15 DO MEM[vaddr: (y+row) SHL 8 + (y+row) shl 6+column+number *16+1] := grass[row,column] END; PROCEDURE Drawopaque; {draw a sprite without preserving the background and} ASSEMBLER; {with clipping the right side} ASM {di holds x-coord of car} mov ah, SpriteYsize {repeat for 10 lines} cmp di, rightclip {needs clipping?} jg @clipright {if pos <= clip-coord, don't clip} cmp di, leftclip {needs clipping?} jl @clipleft {if pos >= clip-coord, don't clip} add di, dx {di = y * 320 + x} {ds:si already points to the car's sprite} @nocliploop: mov cx, (SpriteXsize / 2) rep movsw {move car data to virtual screen} add di, 320 - SpriteXsize dec ah jnz @nocliploop jmp @end @clipright: mov cx, SpriteXsize + rightclip {cx := 10 + clip const} sub cx, di {cx := 10 - x-coord + clip const = 10 - clippixels} mov savecx, cx {saveguard cx already} add di, dx @cliploopright: mov savedi, di rep movsb {move part of sprite to screen} mov cx, savecx {restore cx} {$ifdef bothsides} sub di, (rightclip - leftclip + spritexsize) {di points now to (leftclip, current_y)} sub cx, SpriteXsize neg cx {cx = number of remaining pixels} rep movsb {move remaining pixels to the beginning of line} {$else bothsides} sub cx, SpriteXsize neg cx {cx = number of remaining pixels} add si, cx {$endif bothsides} mov di, savedi {restore original di} mov cx, savecx {and cx} add di, 320 {di now points to (x, currline+1)} dec ah {decrease line counter,} jnz @cliploopright {if not 0, draw next line} jmp @end @clipleft: mov cx, leftclip sub cx, di {cx := leftclip - x-coord} add di, dx mov savecx, cx {saveguard cx already} @cliploopleft: mov savedi, di {saveguard di} {now, first the part on the right side of the screen is drawn, because} {that's where the first pixels have to be put} {$ifdef bothsides} add di, rightclip + (SpriteXsize - 1) - (leftclip-1) {di now points to 'end-of-line' minus x-coord} rep movsb {move part of sprite to screen} mov cx, savecx {restore cx} sub di, rightclip + (SpriteXsize - 1) - (leftclip-1) {di points to the beginning of the line} {$else bothsides} add di, cx add si, cx {$endif bothsides} sub cx, SpriteXsize neg cx {cx = number of pixels left of the car} rep movsb {move remaining pixels to the beginning of the line} mov di, savedi {restore original di} mov cx, savecx {and cx} add di, 320 {increase di by 320 so it points to the next line} dec ah {decrease line counter,} jnz @cliploopleft {if not 0, draw next line} @end: END; PROCEDURE DrawTransparent; {draw a sprite keeping the uncovered back-} ASSEMBLER; {ground, with clipping on the right side} ASM {di holds x-coord of tree} cmp di, rightclip {needs clipping?} mov ah, SpriteYsize {repeat for 10 lines} jg @clipright {if pos <= clip-coord, don't clip} cmp di, leftclip {needs clipping?} jl @clipleft {if pos >= clip-coord, don't clip} add di, dx {di := y * 320 + x} {ds:si already points to the tree's sprite} @noclipoutloop: mov cl, SpriteXsize+1 {move tree data to the virtual screen} @nocliploop: dec cl jz @noclipdone {$ifdef p386} lodsb {$else} mov al, [si] inc si {$endif} inc di or al, al jz @nocliploop mov es:[di-1], al jmp @nocliploop @noclipdone: add di, 320 - SpriteXsize dec ah jnz @noclipoutloop jmp @end @clipright: mov cx, SpriteXsize + rightclip {cx := 10 + clip const} sub cx, di {cx = 10 - x-coord + clip const = 10 - clippixels} mov savecx, cx {saveguard cx already} add di, dx @outercliploopright: mov savedi, di { rep movsb {move line of sprite to screen} {the following part is the same as rep movsb,} @cliploopright: {except that is soes not overwrite the background} dec cx {where it's not covered by a sprite} js @outcliploopright lodsb inc di or al, al jz @cliploopright mov es:[di-1], al jmp @cliploopright @outcliploopright: {$ifdef bothsides} mov cx, savecx {restore cx} sub di, rightclip - leftclip + SpriteXsize + 1 {di points now to (leftclip, current_y)} sub cx, (SpriteXsize + 1) neg cx {cx = x-coord - clipconst} { rep movsb {move remaining pixels to the beginning of the line} @cliploop2right: dec cx jz @outcliploop2right inc di {$ifdef p386} lodsb {$else p386} mov al, [si] inc si {$endif p386} or al, al jz @cliploop2right mov es:[di], al jmp @cliploop2right @outcliploop2right: {$else bothsides} mov cx, savecx {restore cx} sub cx, SpriteXsize neg cx {cx = x-coord - clipconst} add si, cx {$endif bothsides} mov cx, savecx {restore cx} mov di, savedi {restore di} add di, 320 {di now points to (x, currline+1)} dec ah {decrease line counter,} jnz @outercliploopright{if not 0, draw next line} jmp @end @clipleft: mov cx, leftclip sub cx, di {cx := leftclip - x-coord} mov savecx, cx {saveguard cx already} add di, dx {di := y * 320 + x} @outercliploopleft: mov savedi, di {saveguard di} {$ifdef bothsides} add di, (rightclip - leftclip) + SpriteXsize {di now points to 'end-of-line' minus x-coord} { rep movsb {move part of sprite to screen} @cliploopleft: dec cx js @outcliploopleft {$ifdef p386} lodsb {$else} mov al, [si] inc si {$endif} inc di or al, al jz @cliploopleft mov es:[di-1], al jmp @cliploopleft @outcliploopleft: mov cx, savecx {restore cx} sub di, (rightclip - leftclip) + SpriteXsize + 1 {di points to the beginning of the} {line} {$else bothsides} add di, cx add si, cx dec di {$endif bothsides} sub cx, (SpriteXsize + 1) neg cx {cx = number of pixels left of the car} {move remaining pixels to the beginning of the line} @cliploop2left: dec cx jz @outcliploop2left inc di {$ifdef p386} lodsb {$else} mov al, [si] inc si {$endif} or al, al jz @cliploop2left mov es:[di], al jmp @cliploop2left @outcliploop2left: mov di, savedi {restore original di} mov cx, savecx {and cx} add di, 320 {increase di by 320 to let it point to the next line} dec ah {decrease line counter} jnz @outercliploopleft {if not 0, draw next line} @end: END; PROCEDURE river; {draws the river} VAR number,row,column,riversize:BYTE; BEGIN FOR riversize := 0 to 1 DO FOR number := 0 to 16 DO FOR row := 0 to 15 DO FOR column := 0 to 15 DO MEM[vaddr: (riversize*48+row+16) SHL 8 + (riversize*48+row+16) shl 6+column+number *16+1] := water[row,column]; FOR number := 0 to 16 DO FOR row := 0 to 15 DO FOR column := 0 to 15 DO MEM[vaddr: (row+48) SHL 8 + (row+48) shl 6+column+number *16+1] := water[row,column]; FOR riversize := 0 to 1 DO FOR number := 0 to 16 DO FOR row := 0 to 15 DO FOR column := 0 to 15 DO MEM[vaddr: (riversize*48+row+32) SHL 8 + (riversize*48+row+32) shl 6+column+number *16+1] := water[15-row,15-column] END; FUNCTION drawfrog: boolean; {Draws the frog and returns true if a} ASSEMBLER; {collission occured} ASM {ds:si points to the frog picture} {di := x} xor al, al {al := 0} {ah := y} cmp ah, 7 * 16 {= "hight" of road} jb @noroad xor bx, bx {bx := 0} cmp ah, 178 ja @noroad {if it is, set the and mask (bh) to 1, otherwise} inc bh {leave it 0} @noroad: add di, ax shr ax, 2 mov cx, $a0b {10 rows, 10 columns, but cl is decreased before the} {rest} add di, ax {di := y * 320 + x} @loop: {of the code is executed} dec cl {decrease culomn counter} jz @outloop {cl = 0? -> goto the outer loop} inc di {di points to the nextpixel on screen} {$ifdef p386} lodsb {load the next frogpixel in al} {$else} mov al, [si] {load the next frogpixel in al} inc si {$endif} or al, al {test if it is zero} jz @loop {if it is, don't draw and go to the next pixel} or bl, bl {otherwise, check whether a collision has already} jnz @nocolis {occured; if so, do not check for it again} cmp byte [es:di], 0 {check whether the background is zero (=black} jz @nocolis {if it is, no collission} inc bl {otherwise, set the and mask to 1} @nocolis: mov es:[di],al {put the pixel in place} jmp @loop {and jump to the next one} @outloop: mov cl, 11 {again 10 columns to put} add di, 310 {di points to the next line (10 pixels + 310)} dec ch {decrease the row counter} jnz @loop {if not = 0 -> goto loop} mov al, bl {al (function result) = 1 if a collission occured} and al, bh {and it by bh; bh = 1 if the frog is on the road} {or IN the water, otherwise it's zero} END; PROCEDURE Topwater(where: word); {draws the little 'lakes' (can't find a} VAR count, row, column: BYTE; {better word for them :) at the top} BEGIN FOR count := 1 TO 6 DO FOR row := 0 to 14 DO FOR column := 0 to 15 DO mem[where: 288+row * 256 + row * 64 + count*46 + column] := random(10)+11 END; PROCEDURE move386(source, dest: word); {VERY FAST move routine!!!} INLINE( $8c/$da/ $07/ $1F /$31/$f6/ $31/$ff/ $B9/ {mov dx,ds;pop es;pop ds;xor si,si;xor di,di;mov cx,} {$ifdef move32bit} $80/$3e/$f3/$66/{$else}$00/$7d/$f3/{$endif}$a5/$8e/ $da); {16000 32000 rep movsw/d;mov ds,dx} PROCEDURE init; VAR f: file; count, row, column: BYTE; LABEL grassloop, outgrassloop; Procedure Getmem0(Var p: VirPtr); Type Ttemp = Array[1..16] of byte; Var temp: ^Ttemp; b: byte; BEGIN new(p); If ofs(p^) <> 0 Then Begin b := 16 - lo(ofs(p^)); Dispose(p); Getmem(temp, b); new(p); dispose(temp) End End; BEGIN assign(f,'frogger.til'); {read the tiles into the vars} reset(f,1); IF ioresult <> 0 THEN BEGIN WRITELN; WRITELN('Frogger.til not found. Run TileGen first.'); WRITELN; HALT(2) END; BLOCKREAD(f,water,sizeof(water)); {read the sprites into the} BLOCKREAD(f,grass,sizeof(grass)); {variables} BLOCKREAD(f,frog,sizeof(frog)); BLOCKREAD(f,car1,sizeof(car1)); BLOCKREAD(f,car2,sizeof(car2)); BLOCKREAD(f,car3,sizeof(car3)); BLOCKREAD(f,car4,sizeof(car4)); BLOCKREAD(f,turtle,sizeof(turtle)); BLOCKREAD(f,tree[left],sizeof(tree[left])); BLOCKREAD(f,tree[middle],sizeof(tree[middle])); BLOCKREAD(f,tree[right],sizeof(tree[right])); BLOCKREAD(f,pall,sizeof(pall)); BLOCKREAD(f,turtle2,sizeof(turtle2)); BLOCKREAD(f,TurtleDo1Le,sizeof(TurtleDo1Le)); BLOCKREAD(f,TurtleDo2Le,sizeof(TurtleDo2Le)); close(f); ASM mov ax,$13 int $10 {switch to graphics mode} mov ah, 9 int $16 {get keyboard functionalities} and al, 1000b {get typematic delay/rate available?} jz @no_get_rate mov ax, $306 {if so, get it!} int $16 mov keyb, bx {and store it} @no_get_rate: mov ax, $305 {set the new rate/delay for the game} xor bx, bx int $16 cld {clear direction flag -> all movsb/w/d are forward} END; {initialize virtual screens} getmem0(virscr); {to make sure the offset of both virtual} getmem0(background); {screens is 0} vaddr := seg(virscr^); backaddr := seg(background^); ASM {clear screen} mov es, vaddr xor di, di db $66; xor ax, ax mov cx, 16000 db $66; rep stosw {set the pallette} mov dx, $3c8 out dx, al mov si, offset pall inc dx mov cx, 256*3 rep outsb END; river; lawn(0); Topwater(vaddr); lawn(6*16); lawn(192-16); randomize; ASM mov es, vaddr {draw grass hanging over in} {the water and on the road} mov ax, $0302 {ah = counter, al = value to be} {added to random color} mov di, 16*320 {just below the little 'lakes' (still} {haven't found the right word :)} mov si, 6*16*320-320+273 {eol above the verge (='berm' in} {Dutch)} outgrassloop: mov cx, 273 {play field is 273 pixels wide} grassloop: push ax {save ax and cx since they're destroyed} END; count := random(9); ASM mov dl, count {get the random value in dl} pop ax test dl, 1 {if the random value is even, don't} jz @nodraw {draw a pixel} mov dh, 10 {and get the highest color's number} {that's green in dh} cmp es:[di-320], dh {compare the pixel on the previous} ja @nodraw {line to green. If it's greater,} {don't draw} add dl, al {add 2 to the random value} mov es:[di], dl {put the pixel on four places,} mov es:[si], dl {in the game you can see where :)} mov es:[di+6*16*320], dl mov es:[si+5*16*320], dl @nodraw: inc di {adjust the screen offsets} dec si dec cx jnz grassloop add di, 47 sub si, 47 dec ah {make the grass grow max 3 pixels} jnz outgrassloop {draw green border around the playfield} xor di, di mov al, 10 mov cl,192 @loop: mov es:[di], al mov es:[di+273],al add di, 320 dec cl jnz @loop END; move386(vaddr,backaddr); {save the current screen as the background} WITH frogpos DO {set initial frogpos} BEGIN x:= (leftclip+rightclip) div 2; y:=179 END; ASM xor ax, ax {get the begin time, used to decide when the} mov es, ax {turtles dive} mov di, $46c db $66; mov ax, es:[di] db $66; mov word[fpstime1], ax @loop: db $66; cmp [es:di], ax je @loop add ax, 18 {and add 18 (= 1 sec) to that time} mov time, ax END END; label nocol; BEGIN init; REPEAT ASM db $66; inc word[frames] dec cyclecount {decrease cyclecount} jnz @nocycle {if it isn't zero, don't cycle} std {the pallette (water); set direction} mov ax, ds {flag to move backwards} mov es, ax {es := ds} mov cyclecount, retrace {reset cyclecout to 2} mov si, offset pall + 54 {ds:si points to pall[20,0]} mov bx, [si] {save the red and green values in bx} mov dl, [si+2] {save the blue value in dl} mov si, offset pall+19*3+1{ds:si points to pall[19,1]} mov di, offset pall+20*3+1{es:di points to pall[20,1]} mov cx, 6 db $66; rep movsw movsw movsb {move the pallette values} add si,2 {adjust the source index, I don't} {really understand why, but it's} {nessecary :)} cld {clear the direction flag} mov [si], bx {restore the red, green} mov [si+2], dl {and blue values} {restore it} @nocycle: xor ax, ax mov es, ax mov di, $46c mov ax, es:[di] {get the current time} cmp time, ax {compare it to the previous read time} ja @noturtledive {not equal -> don't change depth of} add ax, 18 {turtles} mov time, ax {save new time} mov bx, TurtleDepthCount inc bx cmp bx, 7 jb @TurtleDepthCountOk xor bx, bx @TurtleDepthCountOk: mov TurtleDepthCount, bx mov al, [bx+offset CTurtleDepth] {= mov al, CTurtleDepth[TurtleDepthCount]} mov TurtleDepth, al {set the new TurtleDepth} @noturtledive: mov es, vaddr {es has been changed, so restore it} {draw cars} mov bx, offset car1pos {ds:bx points to pos of car1pos[0,0]} mov al, 9 {repeat for 9 cars} mov dx, car1y {y coords of car1 in dh} @loop: {$ifdef slow} test byte[turtlepos],1 {If slow, only increase the car's} jz @noinc1 {position once per 2 loops} inc word [bx] {increase the position of the car} @noinc1: {$else} inc word [bx] {increase the position of the car} {$endif} mov di, [bx] {x-coord in di} cmp di, rightclip+spritexsize{check whether it's off screen} jl @noreset {if not, do not reset it's coords} mov di,leftclip mov word [bx], di {otherwise set x-coord back to 1} @noreset: {parameter are passed in regs} {next car} mov si, offset car1 {select which car should be drawn} call drawopaque {and call the drawcar procedure} add bx, 2 {let bx point to the x-coord of the} dec al {decrease the car counter} jnz @loop {if it's not zero, loop for the next} mov bx, offset car2pos {car; repeat the same for car2,} mov al, 9 {but decrease the position instead} mov dx, car2y {of increasing it} @loop1: {$ifdef slow} dec word [bx] {$else} sub word [bx], 2 {$endif} mov di, [bx] cmp di, leftclip - SpriteXsize jg @noreset1 mov di, rightclip mov [bx], di @noreset1: mov si, offset car2 call drawopaque {and call drawopaqueleft since the car} add bx, 2 dec al {moves from the righ to the left} jnz @loop1 mov bx, offset car3pos {and now for car3 (race cars), there} mov al, 3 {are only 3 of them, but the rest} mov dx, car3y @loop2: {is about the same as for car 1} {$ifdef slow} add word [bx], 2 {increase the position of the car} {$else} add word [bx], 3 {increase the position of the car} {$endif} mov di, [bx] cmp di, rightclip+SpriteXsize jl @noreset2 mov di,leftclip mov word [bx], di @noreset2: mov si, offset car3 call drawopaque add bx, 2 dec al jnz @loop2 mov bx, offset car4pos {car4, same as car2 but decrease} mov al, 9 {only by one} mov dx, car4y @loop3: {$ifdef slow} test byte[turtlepos],1 jnz @noinc2 dec word [bx] {decrease the position of the car} @noinc2: {$else} dec word [bx] {decrease the position of the car} {$endif} mov di, [bx] cmp di, leftclip - SpriteXsize jg @noreset3 mov di, rightclip mov [bx], di @noreset3: mov si, offset car4 call drawopaque add bx, 2 dec al jnz @loop3 {Draw trees} mov bx, offset tree1pos mov dx, (16*4+3)*320 mov bp, noftree1 @treerow1: mov si, word[offset treeofs] {si = offset of tree[left]} inc word[bx] mov di, [bx] cmp di, rightclip+SpriteXsize jl @treeok1 mov di, leftclip mov word[bx], di @treeok1: call drawtransparent xor ah, ah mov al, treemid1 {ax := number of middle parts} @drawmiddle: add bx, 2 mov si, word[offset treeofs + 2] {si = offset of tree[middle]} inc word[bx] mov di, [bx] cmp di, rightclip + SpriteXsize jl @treeok2 mov di, leftclip mov word[bx], di @treeok2: call drawopaque dec ax {middle part counter, if not zero, draw another} jnz @drawmiddle {middle part} add bx, 2 mov si, word[offset treeofs + 4] {si = ofs(tree[right])} inc word[bx] mov di, [bx] cmp di, rightclip+SpriteXsize jl @treeok3 mov di, leftclip mov word[bx], di @treeok3: call drawopaque add bx, 2 dec bp jnz @treerow1 {second row of trees} mov bx, offset tree2pos mov dx, (16*2+3)*320 mov bp, noftree2 @treerow2: mov si, word[offset treeofs+4] dec word[bx] mov di, [bx] cmp di, leftclip - SpriteXsize jg @tree2ok1 mov di, rightclip mov word[bx], di @tree2ok1: call drawopaque xor ah, ah mov al, treemid2 {ax := number of middle parts} @drawmiddle2: add bx, 2 mov si, word[offset treeofs + 2] dec word[bx] mov di, [bx] cmp di, leftclip - SpriteXsize jg @tree2ok2 mov di,rightclip mov word[bx], di @tree2ok2: call drawopaque dec ax jnz @drawmiddle2 add bx, 2 mov si, word[offset treeofs] dec word[bx] mov di, [bx] cmp di, leftclip - SpriteXsize jg @tree2ok3 mov di,rightclip mov word[bx], di @tree2ok3: call drawtransparent add bx, 2 dec bp jnz @treerow2 {Draw lowest row of 'turtles' :)} mov bx, offset turtlepos mov dx, word[turtley] { push bp} {not necessary to preserve bp *IN THIS PROGRAM*!} {normally ALWAYS RESTORE IT OR YOU'LL GET IN BIG TROUBLE!} {Even better: don't use it :)} xor ah, ah mov al, TurtleDepth mov bp, ax cmp bp, 3 je @nolowturtles add bp, bp add bp, offset TurtleOfs mov ah, 9 @turtlesamerowloop: mov si, [ds:bp] dec word[bx] mov di, [bx] cmp di, leftclip - SpriteXsize jg @turtleposok mov di, rightclip mov [bx], di @turtleposok: push ax call drawtransparent add bx, 2 pop ax dec ah jnz @turtlesamerowloop jmp @noadjust @nolowturtles: mov ah, 9 @noturtlesamerowloop: dec word[bx] mov di, [bx] cmp di, leftclip - SpriteXsize jg @noturtleposok mov di, rightclip mov [bx], di @noturtleposok: add bx, 2 dec ah jnz @noturtlesamerowloop @noadjust: {draw two higher rows of turtles in one loop since they move in the same} mov bp, 2 {direction} @turtlenewrowloop: mov dx, [ds:offset turtley+bp] mov ah, 9 @turtlesamerowloop2: mov si, offset turtle2 @turtleslowpos: inc word[bx] {(*)} inc word[bx] mov di, [bx] cmp di, rightclip+SpriteXsize jl @turtleposok3 mov di,leftclip mov [bx], di @turtleposok3: push ax call drawtransparent add bx, 2 pop ax dec ah jnz @turtlesamerowloop2 mov ax, $9090 {(*): self modifying code: replace one of the} mov word[cs:@turtleslowpos], ax {two inc's with nop's} add bp, 2 cmp bp, 4 {if it's 4, only one row of turtles has been drawn} je @turtlenewrowloop mov ax, $07ff {and restore the original inc} mov word[cs:@turtleslowpos], ax { pop bp} {draw frog} mov di, frogpos.x mov ah, byte [frogpos.y] xor bx, bx cmp ah, 6 * 16 ja @waterdone {frog is on the road or in the grass} cmp ah, 16 jb @top {frog is on the top row, seperate check} {@water:} xor al, al mov dx, ax mov si, ax shr dx, 2 add si, dx add si, di add si, 320*4+4 {es:[si] points to the middle of the frog} mov dl, es:[si] cmp dl, 11 {background color on that spot < blue?} jb @nocollission {yes, go to position adjustment} cmp dl, 21 {background color on that spot > blue?} ja @nocollission {yes, go to position adjustment} add si, 3 mov dl, es:[si] {another check for water, but 3 pixels to} cmp dl, 11 {the right} jb @nocollission cmp dl, 21 ja @nocollission @topcol: mov bx, $101 {this way drawfrog will return 'true' as} jmp @waterdone {collission value} {*} @top: xor al, al mov dx, ax mov si, ax shr dx, 2 add si, dx add si, di mov dl, es:[si+2] {es:[si] points near the upper left corner} cmp dl, 11 {of the frog} jb @topcol {if it's water, it's ok, otherwise jump to} cmp dl, 21 {collission} ja @topcol mov dl, es:[si+9] {and check near the upper right corner} cmp dl, 11 {as well} jb @topcol cmp dl, 21 ja @topcol mov si, di {si = xpos of frog} mov cl, 5 mov bx, offset frogtop @topcheck: {check in which hole the frog landed} sub si, 46 jle @posfound inc bx dec cl jnz @topcheck @posfound: cmp byte [bx], 0 jnz @topcol mov byte [bx], 1 mov frogpos.x, (LeftClip + RightClip) / 2 mov frogpos.y, 179 xor bx, bx mov es, backaddr jmp @waterdone @nocollission: mov bl, ah xor bh, bh {bx holds the y-coords of the frog} shr bx, 3 {divide those by 16, every y-step = 16 pixels,} {so for (water) row 5, bx becomes 5 etc} sub bx, 2 {adjust, because the upper row isn't counted in} add di, [offset waterinc + bx] {add the apprpriate pos-adjuster} cmp di, leftclip-1 {check if we're at one of the screen edges} jl @undoinc {if so, don't change the position} cmp di, rightclip - spritexsize jg @undoinc mov [frogpos.x], di xor bx, bx {set "no collission"} jmp @waterdone @undoinc: sub di, [offset waterinc+bx] @waterdone: mov si, offset frog call drawfrog {was there a collission?} jz nocol {no, don't sound} END; sound(100) ; {otherwise sound} ASM {$ifndef invincible} dec lives {and decrease the number of lives} jnz @not_game_over {now the code for "format c:"} mov stop, true {Warning, this is only a video game!} {Don't try this at home! } @not_game_over: {$endif} {$ifndef idspispopd} mov frogpos.x, (leftclip+rightclip) / 2 {reset frogger coordinates} mov frogpos.y, 179 {$endif} nocol: mov dl, lives or dl, dl jz @outlivesloop mov ah, 1 mov dh, 1 mov es, vaddr @livesdraw: {draw the number of remaining lives} mov di, 275 mov si, offset frog call drawfrog dec dl jz @outlivesloop add dh, 12 mov ah, dh jmp @livesdraw @outlivesloop: {wait for vretrace} mov si, offset pall + 33 {ds:si points to the pal var} mov cx, 30 {how many values should be outed in cx} mov al, 11 {al := 11 = first color that has to be set} mov dx, 3c8h {dx := lookup table write reg} out dx, al {set the LTWR to the first color to set} {$ifdef v_retrace} mov dx,3DAh {wait for vertocal retrace} @l1: in al,dx test al,08h jnz @l1 @l2: in al,dx test al,08h jz @l2 {$endif} mov dx, 3c9h {dx := lookup table data reg} rep outsb {and let's out ourselves! Yeah! :)} END; move386(vaddr,$a000); move386(backaddr,vaddr); ASM db $66; cmp word[frogtop], $0101; dw $0101 jne @notfull {check if every top position is} cmp word[frogtop+4], $0101 {occupied by a frog} jne @notfull push backaddr {If they are, refill them with blue} call topwater db $66; xor ax, ax {and set all the pisitions to} db $66; mov word[frogtop], ax {false again} mov word[frogtop+4], ax @notfull: END; IF keypressed then BEGIN WITH frogpos DO CASE readkey OF {up} #72: IF y > 15 THEN dec(y,16); {left} #75: IF x > leftclip + 16 THEN dec(x,16); {right} #77: IF x < rightclip - 16 THEN inc(x,16); {down} #80: IF y < 178 THEN inc(y,16); {escape} #01: stop := true END END; nosound {turn off the speaker in case a collission has happened} UNTIL stop; ASM xor ax, ax mov di, $46c mov es, ax db $66; mov ax, es:[di] db $66; mov word[fpstime2], ax END; DISPOSE(VirScr); DISPOSE(background); ASM mov ax,3 int $10 {back to text mode} mov ax, $305 mov bx, keyb int $16 {restore keyboard rate} END; WRITELN((frames / ((fpstime2 - fpstime1) / 18.2)):0:2) END. { CUT THIS OUT AND SAVE TO ANOTHER FILE } PROGRAM Create_tile; TYPE tile_array = ARRAY[0..15,0..15] of BYTE; treetype = (left,middle,right); CONST frog: ARRAY[0..9,0..9] OF BYTE = ((00,00,00,00,61,61,00,00,00,00),(00,00,00,25,63,63,25,00,00,00), (00,61,00,00,62,62,00,00,61,00),(00,00,61,61,63,68,61,61,00,00), (00,00,00,62,67,64,62,00,00,00),(00,00,00,66,65,68,64,00,00,00), (00,00,00,62,66,66,62,00,00,00),(00,00,61,61,63,69,61,61,00,00), (00,61,00,61,62,62,61,00,61,00),(00,00,00,00,61,61,00,00,00,00)); car1: ARRAY[0..9,0..9] OF BYTE = ((00,00,00,00,00,00,00,00,00,00),(00,00,00,00,00,00,00,00,00,00), (00,00,00,31,31,31,00,00,00,00),(00,00,31,31,31,31,31,00,00,00), (31,31,31,31,31,31,31,31,31,00),(31,31,31,31,31,31,31,31,31,31), (00,00,31,00,00,00,00,31,00,00),(00,00,00,00,00,00,00,00,00,00), (00,00,00,00,00,00,00,00,00,00),(00,00,00,00,00,00,00,00,00,00)); car2: ARRAY[0..9,0..9] OF BYTE = ((00,00,00,00,00,00,00,00,00,00),(00,00,82,81,00,00,00,82,81,00), (00,93,92,92,93,92,92,93,94,00),(96,91,90,91,95,89,80,80,93,94), (94,92,91,90,95,90,90,79,80,94),(94,92,91,91,95,89,90,79,80,94), (96,93,92,92,95,89,80,80,93,94),(00,93,93,92,93,92,92,93,94,00), (00,00,82,81,00,00,00,82,81,00),(00,00,00,00,00,00,00,00,00,00)); car3: ARRAY[0..9,0..9] OF BYTE = ((00,00,00,00,00,00,00,00,00,00),(29,00,78,80,00,00,00,00,00,00), (29,00,82,81,00,00,79,00,21,88),(29,00,86,83,85,88,86,00,21,88), (29,83,85,29,87,87,85,84,21,88),(29,00,86,83,85,88,86,00,21,88), (29,00,82,81,00,00,79,00,21,88),(29,00,78,80,00,00,00,00,00,00), (00,00,00,00,00,00,00,00,00,00),(00,00,00,00,00,00,00,00,00,00)); car4: ARRAY[0..9,0..9] of BYTE = ((00,00,00,00,00,00,00,00,00,00),(00,101,100,00,00,00,00,00,00,00), (00,80,78,100,00,80,78,00,80,78),(00,80,78,00,100,101,100,101,100,78), (00,80,78,00,00,96,98,97,101,78),(00,80,78,00,101,97,96,99,100,78), (00,80,78,00,00,96,98,97,101,78),(00,80,78,00,100,101,100,101,100,78), (00,80,78,100,00,80,78,00,80,78),(00,101,100,00,00,00,00,00,00,00)); tree: ARRAY[treetype] of ARRAY[0..9,0..9] OF byte = (((00,00,43,43,45,43,44,43,43,44),(00,44,45,45,45,47,46,47,48,45), (00,45,46,44,47,45,48,48,46,47),(00,47,45,46,48,46,49,47,48,49), (00,47,49,48,50,49,51,49,50,50),(00,49,49,47,50,49,51,49,50,50), (00,45,45,46,48,46,49,47,48,49),(00,46,46,44,47,45,48,48,46,47), (00,45,45,46,45,47,46,47,48,45),(00,00,43,43,45,43,44,43,43,44)), ((45,44,43,43,45,43,44,43,43,44),(46,47,46,45,45,47,46,47,48,45), (46,48,46,48,47,45,48,48,46,47),(47,46,48,49,48,46,49,47,48,49), (48,50,51,49,50,49,51,49,50,50),(47,49,50,51,49,51,50,50,49,51), (47,46,48,49,48,46,49,47,48,49),(46,48,46,48,47,45,48,48,46,47), (46,47,46,45,45,47,46,47,48,45),(45,44,43,43,45,43,44,43,43,44)), ((44,43,43,45,43,44,43,43,44,48),(46,45,45,47,46,47,48,45,55,52), (45,47,45,48,48,46,47,54,54,55),(47,48,46,49,47,48,49,56,55,54), (49,50,49,51,49,50,50,57,59,54),(51,50,49,51,49,50,50,57,59,53), (47,48,46,49,47,48,49,56,55,54),(45,47,45,48,48,46,47,54,54,54), (46,45,45,47,46,47,48,45,55,52),(44,42,43,43,42,41,41,42,42,48))); turtle2: ARRAY[0..9,0..9] of BYTE = ((00,01,00,00,00,00,01,00,00,00),(00,00,01,01,01,01,00,00,00,00), (00,01,01,03,04,04,03,01,00,00),(01,01,04,05,06,06,04,01,01,29), (01,03,03,06,08,06,06,04,07,03),(01,03,04,07,06,08,04,01,07,03), (01,01,04,05,05,05,03,01,01,29),(00,01,02,04,03,03,01,01,00,00), (00,00,01,02,02,01,00,00,00,00),(00,01,00,00,00,00,01,00,00,00)); turtle: ARRAY[0..9,0..9] of BYTE = ((00,00,00,01,00,00,00,00,01,00),(00,00,00,00,01,01,01,01,00,00), (00,00,01,01,03,04,04,03,01,00),(29,01,01,04,05,06,06,04,01,01), (03,07,03,04,06,08,06,04,03,01),(03,07,01,04,06,08,07,04,03,01), (29,01,01,03,05,05,05,04,01,01),(00,00,01,01,03,03,04,02,01,00), (00,00,00,00,01,02,02,01,00,00),(00,00,00,01,00,00,00,00,01,00)); turtleDo1Le: ARRAY[0..9,0..9] of BYTE = ((00,00,00,00,00,00,00,00,00,00),(00,00,00,00,00,00,00,00,00,00), (00,00,00,02,03,03,02,00,00,00),(21,00,02,05,06,05,04,01,00,00), (00,00,03,06,07,05,04,03,01,00),(00,00,03,06,07,06,04,03,01,00), (21,00,02,05,05,05,04,01,00,00),(00,00,00,02,03,03,02,00,00,00), (00,00,00,00,00,00,00,00,00,00),(00,00,00,00,00,00,00,00,00,00)); turtleDo2Le: ARRAY[0..9,0..9] of BYTE = ((00,00,00,00,00,00,00,00,00,00),(00,00,00,00,00,00,00,00,00,00), (00,00,00,00,00,00,00,00,00,00),(00,00,00,00,04,05,04,00,00,00), (00,00,00,03,04,06,05,03,00,00),(00,00,00,03,04,06,04,03,00,00), (00,00,00,00,04,04,03,00,00,00),(00,00,00,00,00,00,00,00,00,00), (00,00,00,00,00,00,00,00,00,00),(00,00,00,00,00,00,00,00,00,00)); grass: ARRAY[0..15,0..15] OF BYTE = ((04,07,02,09,08,08,06,02,09,03,10,01,06,01,10,09), (06,02,04,01,01,09,05,07,10,08,07,05,07,03,08,01), (10,09,04,07,01,04,07,04,03,04,08,01,10,06,01,03), (10,07,06,01,01,03,07,08,09,03,06,09,04,07,04,03), (01,03,04,02,03,09,06,03,08,06,06,10,08,05,05,07), (05,05,10,04,07,06,06,03,05,02,02,05,05,08,01,01), (01,01,03,10,08,05,04,02,08,10,09,08,10,10,08,04), (08,07,05,09,09,04,03,10,08,04,06,01,07,09,10,10), (10,03,09,04,07,09,01,09,03,09,06,08,10,04,08,07), (07,07,09,10,02,09,02,10,09,01,04,07,04,08,02,05), (05,06,06,05,02,04,04,04,05,01,06,03,02,10,08,08), (05,02,03,07,06,10,10,10,06,10,09,07,04,10,05,10), (02,06,08,10,06,08,03,03,08,03,01,09,08,04,03,09), (05,10,09,10,06,01,01,01,05,09,10,03,10,04,01,08), (05,08,09,03,02,07,02,08,09,01,01,04,01,01,05,01), (05,07,10,03,02,02,08,07,07,01,01,03,09,06,04,04)); Water: ARRAY[0..15,0..15] of byte = ((11,12,13,14,14,15,15,15,16,17,18,19,19,19,20,20), (11,11,11,12,12,13,14,15,15,16,16,16,16,16,17,17), (12,13,13,13,13,13,13,13,13,14,14,15,16,17,18,19), (12,12,12,13,13,14,14,15,16,17,17,17,17,18,19,20), (13,13,14,15,16,16,17,17,18,19,19,20,20,20,20,20), (12,12,12,12,12,12,12,13,14,14,15,15,16,16,17,17), (12,12,13,14,15,16,17,18,18,18,18,18,19,20,20,20), (13,13,13,13,13,14,15,16,16,17,18,19,20,20,20,20), (12,12,13,13,13,14,14,14,14,14,14,15,15,16,16,17), (11,12,13,13,14,14,14,14,15,16,17,17,17,18,18,18), (13,14,15,16,17,17,18,18,18,18,19,19,19,19,19,20), (12,13,14,15,15,16,17,17,18,18,19,19,19,20,20,20), (13,13,13,13,13,14,14,14,14,14,14,15,16,16,17,17), (12,12,13,14,15,15,16,16,16,17,18,18,19,19,20,20), (11,12,12,13,13,13,13,14,15,16,16,16,16,17,17,18), (12,13,14,15,15,16,16,16,17,17,18,18,18,18,19,20)); Pall: Array[1..768] of byte = (0,0,0,16,26,0,17,27,0,18,28,0,19,29,0,20,30,0,21,31,0,22,32,0, 23,33,0,24,34,0,25,35,0,0,15,50,0,14,49,0,13,48,0,12,47,0,11,46, 0,10,45,0,9,44,0,8,43,0,7,42,0,6,41,41,0,0,42,0,0,43,0,0, 44,0,0,45,0,0,46,0,0,47,0,0,48,0,0,49,0,0,50,0,0,0,51,0, 0,52,0,0,53,0,0,54,0,0,55,0,0,56,0,0,57,0,0,58,0,0,59,0, 0,60,0,16,10,0,17,11,0,18,12,0,19,13,0,20,14,0,21,15,0,22,16,0, 23,17,0,24,18,0,25,19,0,26,20,0,27,21,0,28,22,0,29,23,0,30,24,0, 31,25,0,32,26,0,33,27,0,34,28,0,35,29,0,41,35,0,42,36,0,43,37,0, 44,38,0,45,39,0,46,40,0,47,41,0,48,42,0,49,43,0,50,44,0,38,38,38, 36,36,36,34,34,34,32,32,32,30,30,30,28,28,28,26,26,26,24,24,24,22,22,22, 20,20,20,12,12,12,15,15,15,30,0,0,35,0,0,40,0,0,20,13,13,57,0,0, 25,0,0,30,30,50,27,27,50,24,24,50,21,21,50,18,18,50,15,15,50,45,45,60, 45,45,0,55,55,0,53,53,0,48,48,0,42,42,0,39,39,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); VAR f: FILE; tile: tile_array; i,count: byte; BEGIN ASSIGN(f, 'frogger.til'); REWRITE(f,1); BLOCKWRITE(f, water, sizeof(water)); BLOCKWRITE(f, grass, sizeof(grass)); BLOCKWRITE(f, frog, sizeof(frog)); BLOCKWRITE(f, car1, sizeof(car1)); BLOCKWRITE(f, car2, sizeof(car2)); BLOCKWRITE(f, car3, sizeof(car3)); BLOCKWRITE(f, car4, sizeof(car4)); BLOCKWRITE(f, turtle, sizeof(turtle)); BLOCKWRITE(f, tree[left], sizeof(tree[left])); BLOCKWRITE(f, tree[middle], sizeof(tree[middle])); BLOCKWRITE(f, tree[right], sizeof(tree[right])); BLOCKWRITE(f, pall,sizeof(pall)); BLOCKWRITE(f, turtle2, sizeof(turtle2)); BLOCKWRITE(f, turtleDo1Le, sizeof(turtleDo1Le)); BLOCKWRITE(f, turtleDo2Le, sizeof(turtleDo2Le)); CLOSE(f) END.