Programming by Example

 

A BB4W Compendium

freeman69@gmx.com

IDIC BBC_Owl2 M&P

Dragonfly

Dragonfly is a vertically scrolling game designed to demonstrate the use of vector mathematics. The aim is to visit the flowers for extra points while remaining aloft by catching smaller insects. Avoid the camouflaged frogs!

 

         Z = Left

         X = Right

         K = Forward

         M = Backward

 

    This game includes:

 

         Evinrude the dragonfly

         Rocks (collision detection using point-within-convex-polygon test)

         Frogs (turn towards Evinrude)

         Flowers that puff pollen particles

         Flies

 

  10 MODE 9:OFF

  20 *REFRESH OFF

  30 *FONT Arial,16

  40 COLOUR 12,0,0,64:COLOUR 11,0,216,0:COLOUR 15,160,160,160

  50

  60 MAXNODES=32

  70 DIM node(MAXNODES,3):FOR a=0 TO 26:READ node(a,0),node(a,1):NEXT

  80 FOR a=0 TO 4

  90   r=RND(20)/5+16:REM Randomly irregular pentagon rock

 100   node(a+27,0)=SIN(RAD(a*72))*r

 110   node(a+27,1)=COS(RAD(a*72))*r

 120 NEXT

 130 node(32,0)=node(27,0):node(32,1)=node(27,1)

 140 MAXOBJS=200

 150 DIM obj{(MAXOBJS) status,type,x,y,angle,sc,r,fc,frog,lash}

 160 DIM stack(MAXOBJS):FOR a=0 TO MAXOBJS:stack(a)=a:NEXT

 170 stackptr=0

 180 DIM ix{(4) x,y}:REM Intersecting lines data block

 190

 200 REM Lily pads & rocks are created at regular intervals in horizontal bands

 210 bandystart=1024+7*20:bandyvel=-4:bandy=bandystart:bandwidth=100

 220 probofnew=11:REM controls probability of lily pad or rock appearing

 230

 240 flycount=0:flytarget=10

 250 LICKSIZE=160:REM Frog tongue length

 260 evinrude=-1:dead=FALSE:evinergy=100

 270 hiscore=0:score=0:lives=0

 280

 290 COLOUR 128+12

 300 REPEAT

 310   TIME=0:CLS

 320  

 330   PROCupstream:REM Create objects upstream

 340   PROCthink:REM Control objects

 350   REM Game Over or first game

 360   IF lives=0 OR lives=1 AND dead THEN

 370     IF score>hiscore hiscore=score

 380     GCOL 0,7

 390     MOVE 440,512:VDU 5:PRINT"Press SPACE to play":VDU4

 400     IF INKEY(-99) THEN

 410       PROCele:evinrude=FNnewobj(6,640,60,0,0,50):dead=FALSE

 420       evinax=0:evinay=0:evinvx=0:evinvy=0:evinergy=100

 430       level=0:probofnew=11:bandyvel=-4

 440       score=0:lives=3

 450     ENDIF

 460   ENDIF

 470   REM Respawn Evinrude

 480   IF evinrude=-1 AND lives>0 THEN

 490     PROCele:evinrude=FNnewobj(6,640,60,0,0,50):dead=FALSE

 500     evinax=0:evinay=0:evinvx=0:evinvy=0:evinergy=100

 510   ENDIF

 520   REM Energy level display

 530   IF evinergy>100 evinergy=100

 540   VDU 23,23,3;0;0;0;

 550   GCOL 0,0:RECTANGLEFILL 50,512,20,200

 560   IF evinergy<40 GCOL 0,1 ELSE GCOL 0,2

 570   RECTANGLEFILL 50,512,20,evinergy*2

 580   GCOL 0,7:RECTANGLE 50,512,20,200

 590  

 600   VDU5

 610   FOR a=0 TO 1

 620     IF a=0 GCOL 0,0 ELSE GCOL0,7

 630     MOVE 100-a*4,1023-a*4:PRINT"Score: ";score

 640     MOVE 500-a*4,1023-a*4:PRINT"Lives: ";lives

 650     MOVE 900-a*4,1023-a*4:PRINT"Hi-Score: ";hiscore

 660   NEXT

 670   VDU4

 680   *REFRESH

 690   WHILE TIME<4:ENDWHILE

 700   *FX21

 710 UNTIL FALSE

 720 END

 730

 740 REM Lily 0 offset,10 nodes

 750 DATA 0,-18,0,10,0,0,10,10,-10,10

 760 DATA 0,-12,14,0,-14,0,-4,21,4,21

 770 REM Frog 10 offset,6 nodes

 780 DATA -40,40,40,-40,-40,-40,40,40

 790 DATA 0,-10,0,15

 800 REM Evinrude 16 offset,6 nodes

 810 DATA 0,-50,0,20

 820 DATA -40,20,40,-20,-40,-20,40,20

 830 REM Fly 22 offset,5 nodes

 840 DATA 0,-22,0,8

 850 DATA 0,0,-15,-15,15,-15

 860 REM Rock 27 offset,6 nodes

 870

 880 DEF PROCprespin(tptr,n,x,y,angle,scale)

 890 LOCAL c,d

 900 FOR c=0 TO n-1

 910   d=c+tptr

 920   node(d,2)=x+FNrotx(node(d,0),node(d,1),angle)*scale

 930   node(d,3)=y+FNroty(node(d,0),node(d,1),angle)*scale

 940 NEXT

 950 ENDPROC

 960

 970 DEF PROClily(x,y,angle,scale)

 980 PROCprespin(0,10,x,y,angle,scale)

 990 GCOL 0,2:VDU 23,23,3;0;0;0;

1000 CIRCLEFILL x,y,20*scale

1010 GCOL 0,3:CIRCLE x,y,20*scale

1020 GCOL 0,10

1030 LINE node(0,2),node(0,3),node(1,2),node(1,3)

1040 LINE node(2,2),node(2,3),node(3,2),node(3,3)

1050 LINE node(2,2),node(2,3),node(4,2),node(4,3)

1060 LINE node(5,2),node(5,3),node(6,2),node(6,3)

1070 LINE node(5,2),node(5,3),node(7,2),node(7,3)

1080 GCOL 0,12

1090 MOVE node(1,2),node(1,3)

1100 MOVE node(8,2),node(8,3)

1110 PLOT 85,node(9,2),node(9,3)

1120 ENDPROC

1130

1140 DEF PROCfrog(x,y,angle)

1150 PROCprespin(10,6,x,y,angle,1)

1160 VDU 23,23,11;0;0;0;

1170 LINE node(10,2),node(10,3),node(11,2),node(11,3)

1180 LINE node(12,2),node(12,3),node(13,2),node(13,3)

1190 CIRCLEFILL node(14,2),node(14,3),40

1200 CIRCLEFILL node(15,2),node(15,3),35

1210 ENDPROC

1220

1230 DEF PROCfrog2(x,y,angle)

1240 GCOL 0,10:PROCfrog(x+4,y+4,angle)

1250 GCOL 0,11:PROCfrog(x,y,angle)

1260 ENDPROC

1270

1280 DEF PROCevinrude(x,y,angle)

1290 PROCprespin(16,6,x,y,angle,1)

1300 GCOL 0,4:VDU 23,23,9;0;0;0;

1310 LINE node(16,2),node(16,3),node(17,2),node(17,3)

1320 GCOL 0,14

1330 LINE node(18,2),node(18,3),node(19,2),node(19,3)

1340 LINE node(20,2),node(20,3),node(21,2),node(21,3)

1350 GCOL 0,4:VDU 23,23,3;0;0;0;

1360 LINE node(16,2),node(16,3),node(17,2),node(17,3)

1370 ENDPROC

1380

1390 DEF PROCfly(x,y,angle)

1400 PROCprespin(22,5,x,y,angle,1)

1410 GCOL 0,5:VDU 23,23,7;0;0;0;

1420 LINE node(22,2),node(22,3),node(23,2),node(23,3)

1430 GCOL 0,6

1440 LINE node(24,2),node(24,3),node(25,2),node(25,3)

1450 LINE node(24,2),node(24,3),node(26,2),node(26,3)

1460 GCOL 0,5:VDU 23,23,3;0;0;0;

1470 LINE node(22,2),node(22,3),node(23,2),node(23,3)

1480 ENDPROC

1490

1500 DEF PROCrock(x,y,angle,scale)

1510 PROCprespin(27,6,x,y,angle,scale)

1520 GCOL 0,15

1530 MOVE node(27,2),node(27,3):MOVE node(28,2),node(28,3)

1540 PLOT 85,node(31,2),node(31,3)

1550 PLOT 85,node(29,2),node(29,3)

1560 PLOT 85,node(30,2),node(30,3)

1570 GCOL 0,7:VDU 23,23,3;0;0;0;

1580 FOR c=0 TO 4

1590   LINE node(27+c,2),node(27+c,3),node(27+c+1,2),node(27+c+1,3)

1600 NEXT

1610 ENDPROC

1620

1630 DEF PROCflower(x,y,a,col,scale)

1640 LOCAL d,f,g,r,k,c

1650 scale*=20:d=51.4:f=0.66

1660 GCOL 0,col:REM Pollen

1670 RECTANGLEFILL x-scale/2,y-scale/2,scale

1680 FOR g=1 TO 2

1690   GCOL 0,VAL(MID$("15",g,1)):REM Petal colours

1700   r=scale-scale/8*(g-1):k=g*d/2

1710   FOR c=0 TO 6

1720     MOVE x+SIN(RAD(a+c*d-d+k))*r*f,y+COS(RAD(a+c*d-d+k))*r*f

1730     MOVE x+SIN(RAD(a+c*d+d+k))*r*f,y+COS(RAD(a+c*d+d+k))*r*f

1740     PLOT 85,x+SIN(RAD(a+c*d+k))*r,y+COS(RAD(a+c*d+k))*r

1750   NEXT

1760 NEXT

1770 ENDPROC

1780

1790 DEF PROCpollen(x,y)

1800 GCOL 0,7:RECTANGLEFILL x-4,y-4,8

1810 ENDPROC

1820

1830 DEF FNrotx(x,y,angle)

1840 =COS(RAD(angle))*x-SIN(RAD(angle))*y

1850

1860 DEF FNroty(x,y,angle)

1870 =SIN(RAD(angle))*x+COS(RAD(angle))*y

1880

1890 DEF FNnewobj(type,x,y,angle,sc,r)

1900 LOCAL p

1910 IF stackptr>=MAXOBJS THEN

1920   =-1

1930 ELSE

1940   p=stack(stackptr):stackptr+=1

1950   obj{(p)}.status=TRUE

1960   obj{(p)}.type=type

1970   obj{(p)}.x=x

1980   obj{(p)}.y=y

1990   obj{(p)}.angle=angle

2000   obj{(p)}.sc=sc

2010   obj{(p)}.r=r

2020   =p

2030 ENDIF

2040

2050 DEF PROCkillobj(p)

2060 stackptr-=1:stack(stackptr)=p

2070 obj{(p)}.status=FALSE

2080 IF p=evinrude evinrude=-1:lives-=1

2090 IF obj{(p)}.type=5 flycount-=1

2100 ENDPROC

2110

2120 DEF PROCele

2130 LOCAL a

2140 FOR a=0 TO MAXOBJS:stack(a)=a:obj{(a)}.status=FALSE:NEXT

2150 stackptr=0:flycount=0

2160 ENDPROC

2170

2180 DEF PROCthink

2190 LOCAL t,a,x,y,angle,tx,ty,achg,d

2200 IF evinrude>-1 tx=obj{(evinrude)}.x:ty=obj{(evinrude)}.y+10

2210 FOR t=0 TO 3

2220   FOR a=0 TO MAXOBJS

2230     IF obj{(a)}.status THEN

2240       x=obj{(a)}.x:y=obj{(a)}.y:angle=obj{(a)}.angle

2250       CASE t OF

2260         WHEN 0

2270           CASE obj{(a)}.type OF

2280             WHEN 1

2290               REM Rocks (may collide with dragonfly)

2300               obj{(a)}.y+=bandyvel

2310               PROCrock(x,y,angle,obj{(a)}.sc)

2320               IF evinrude>-1 THEN

2330                 IF FNinpolygon(tx,ty) dead=TRUE

2340               ENDIF

2350             WHEN 2

2360               REM Lilies

2370               obj{(a)}.y+=bandyvel

2380               PROClily(x,y,angle,obj{(a)}.sc)

2390           ENDCASE

2400         WHEN 1

2410           CASE obj{(a)}.type OF

2420             WHEN 3

2430               REM Flowers may create a puff of pollen when touched

2440               obj{(a)}.y+=bandyvel

2450               IF evinrude>-1 AND obj{(a)}.fc=7 THEN

2460                 IF SQR((tx-x)^2+(ty-y)^2)<obj{(a)}.sc*8 THEN

2470                   obj{(a)}.fc=4:PROCpuff(x,y,obj{(a)}.sc*8)

2480                 ENDIF

2490               ENDIF

2500               PROCflower(x,y,angle,obj{(a)}.fc,obj{(a)}.sc)

2510             WHEN 4

2520               REM Frogs turn towards dragonfly and flick tongue when close

2530               obj{(a)}.y+=bandyvel

2540               IF evinrude>-1 AND NOT dead THEN

2550                 achg=FNanglebetween(tx-x,ty-y,-SIN(RAD(angle)),COS(RAD(angle)))

2560                 IF ABS(achg)>5 achg=5*SGN(achg)

2570                 angle+=achg

2580                 obj{(a)}.angle=angle

2590                 IF obj{(a)}.sc=0 AND ABS(achg)<3 THEN

2600                   d=SQR((tx-x)^2+(ty-y)^2)

2610                   IF d<LICKSIZE+40 PROCtongue(a,x,y,angle)

2620                 ENDIF

2630               ENDIF

2640               PROCfrog2(x,y,angle)

2650             WHEN 8

2660               REM Frog tongues are processed separately

2670               obj{(a)}.y+=bandyvel

2680               obj{(a)}.fc-=1:REM Tongue countdown

2690               IF obj{(a)}.fc<=0 THEN

2700                 IF obj{(obj{(a)}.frog)}.status THEN

2710                   obj{(obj{(a)}.frog)}.sc=0:REM Update frog about tongue (in)

2720                 ENDIF

2730                 IF obj{(a)}.lash AND evinrude>-1 THEN

2740                   obj{(evinrude)}.x=x:REM Draw Evinrude into Frog's mouth

2750                   obj{(evinrude)}.y=y

2760                   tx=obj{(evinrude)}.x:ty=obj{(evinrude)}.y+10

2770                 ENDIF

2780                 PROCkillobj(a):REM Tongue withdraws

2790               ELSE

2800                 VDU 23,23,3;0;0;0;:GCOL 0,1

2810                 LINE x,y,x-SIN(RAD(angle))*LICKSIZE,y+COS(RAD(angle))*LICKSIZE

2820               ENDIF

2830           ENDCASE

2840         WHEN 2

2850           CASE obj{(a)}.type OF

2860             WHEN 5

2870               REM Flies (may collide with dragonfly)

2880               x+=SIN(RAD(obj{(a)}.angle))*bandyvel*2

2890               y-=COS(RAD(obj{(a)}.angle))*bandyvel*2

2900               obj{(a)}.x=x

2910               obj{(a)}.y=y

2920               PROCfly(x,y,angle)

2930               IF evinrude>-1 THEN

2940                 IF SQR((tx-x)^2+(ty-y)^2)<30 THEN

2950                   PROCkillobj(a):evinergy+=10:PROCincreasescore(5)

2960                 ENDIF

2970               ENDIF

2980             WHEN 7

2990               REM Pollen

3000               obj{(a)}.y+=bandyvel:angle-=1

3010               IF angle=0 PROCkillobj(a) ELSE obj{(a)}.angle=angle

3020               PROCpollen(x,y)

3030           ENDCASE

3040         WHEN 3

3050           CASE obj{(a)}.type OF

3060             WHEN 6

3070               REM Dragonfly

3080               IF dead obj{(a)}.y+=bandyvel ELSE PROCsteer

3090               x=obj{(a)}.x:y=obj{(a)}.y:angle=obj{(a)}.angle

3100               PROCevinrude(x,y,angle)

3110           ENDCASE

3120       ENDCASE

3130       IF obj{(a)}.y<-obj{(a)}.r PROCkillobj(a)

3140     ENDIF

3150   NEXT

3160 NEXT

3170 ENDPROC

3180

3190 DEF PROCtongue(f,x,y,angle)

3200 LOCAL d,tx,ty

3210 x-=SIN(RAD(angle))*40

3220 y+=COS(RAD(angle))*40

3230 d=FNnewobj(8,x,y,angle,0,LICKSIZE)

3240 IF d>-1 THEN

3250   obj{(f)}.sc=1:REM Frog updated about tongue status (out)

3260   obj{(d)}.fc=5:REM Countdown

3270   obj{(d)}.frog=f:REM Retain link to frog

3280   REM Check for contact with Evinrude

3290   ix{(0)}.x=x

3300   ix{(0)}.y=y

3310   ix{(1)}.x=x-SIN(RAD(angle))*LICKSIZE

3320   ix{(1)}.y=y+COS(RAD(angle))*LICKSIZE

3330   ix{(2)}.x=obj{(evinrude)}.x

3340   ix{(2)}.y=obj{(evinrude)}.y+20

3350   ix{(3)}.x=obj{(evinrude)}.x

3360   ix{(3)}.y=obj{(evinrude)}.y-50

3370   IF FNintersect THEN

3380     obj{(d)}.lash=TRUE:dead=TRUE

3390   ELSE

3400     obj{(d)}.lash=FALSE

3410   ENDIF

3420 ENDIF

3430 ENDPROC

3440

3450 DEF PROCupstream

3460 LOCAL a,d,x,y,scale,r,angle,tx,ty

3470 bandy+=bandyvel

3480 IF bandy<bandystart-bandwidth THEN

3490   bandy+=bandwidth

3500   FOR a=1 TO 10:REM Max objs/attempts per band

3510     IF RND(probofnew)>10 THEN

3520       REM Scale of lily or rock

3530       scale=RND(4)+3:r=scale*20

3540       x=RND(1280-r*2)+r:y=RND(bandwidth)+bandy

3550       angle=RND(360)

3560       IF FNnooverlap(x,y,r) THEN

3570         REM 66:33 chance of lily or rock

3580         IF RND(3)=1 THEN

3590           d=FNnewobj(1,x,y,angle,scale,r)

3600         ELSE

3610           d=FNnewobj(2,x,y,angle,scale,r)

3620           IF d>-1 THEN

3630             IF RND(10)>5 THEN

3640               scale=RND(4)+1:REM Flower

3650               IF scale>obj{(d)}.sc scale=obj{(d)}.sc

3660               r=scale*20

3670               d=FNnewobj(3,x,y,RND(360),scale,r):IF d<>-1 obj{(d)}.fc=7

3680             ELSE

3690               IF RND(2)=1 d=FNnewobj(4,x,y,RND(360),0,60):REM Frog

3700             ENDIF

3710           ENDIF

3720         ENDIF

3730       ENDIF

3740     ENDIF

3750   NEXT

3760 ENDIF

3770 IF flycount<flytarget AND RND(30)=1 THEN

3780   x=RND(1280):y=1050

3790   tx=RND(1000)+140:ty=RND(400)

3800   a=FNanglebetween(tx-x,ty-y,0,1)

3810   d=FNnewobj(5,x,y,a,0,22)

3820   IF d<>-1 flycount+=1

3830 ENDIF

3840 ENDPROC

3850

3860 DEF FNnooverlap(x,y,r)

3870 LOCAL clr,a,xa,ya

3880 clr=TRUE:a=0

3890 REPEAT

3900   IF obj{(a)}.status AND obj{(a)}.type<3 THEN

3910     xa=obj{(a)}.x:ya=obj{(a)}.y

3920     IF SQR((x-xa)^2+(y-ya)^2)<=r+obj{(a)}.sc*20 clr=FALSE

3930   ENDIF

3940   a+=1

3950 UNTIL a>MAXOBJS OR NOT clr

3960 =clr

3970

3980 DEF PROCsteer

3990 LOCAL accel,x,y

4000 IF evinergy>0 evinergy-=0.1

4010 accel=0.5*evinergy/100

4020 IF INKEY(-98) AND evinax>-2 evinax-=accel

4030 IF INKEY(-67) AND evinax<2 evinax+=accel

4040 IF INKEY(-102) AND evinay>-2 evinay-=accel

4050 IF INKEY(-71) AND evinay<2 evinay+=accel

4060 evinvx+=evinax:evinvy+=evinay

4070 evinax*=0.9:evinay*=0.9

4080 evinvx*=0.9:evinvy*=0.9

4090 IF ABS(evinvx)>16 evinvx=16*SGN(evinvx)

4100 IF ABS(evinvy)>16 evinvy=16*SGN(evinvy)

4110 x=obj{(evinrude)}.x+evinvx

4120 y=obj{(evinrude)}.y+evinvy

4130 IF x<0 x=0:evinvx*=-1:evinax*=-1

4140 IF x>1279 x=1279:evinvx*=-1:evinax*=-1

4150 IF y<0 y=0:evinvy*=-1:evinay*=-1

4160 IF y>1023 y=1023:evinvy*=-1:evinay*=-1

4170 obj{(evinrude)}.x=x

4180 obj{(evinrude)}.y=y

4190 ENDPROC

4200

4210 DEF FNinpolygon(x,y)

4220 LOCAL a,inside,xe,ye,xp,yp

4230 a=27:inside=TRUE

4240 REPEAT

4250   xe=x-node(a,2)

4260   ye=y-node(a,3)

4270   xp=node(a,3)-node(a+1,3)

4280   yp=node(a+1,2)-node(a,2)

4290   IF (xe*xp+ye*yp)>0 inside=FALSE

4300   a+=1

4310 UNTIL a>31 OR NOT inside

4320 =inside

4330

4340 DEF FNanglebetween(xa,ya,xb,yb)

4350 LOCAL ma,mb,adotb,ndotb

4360 IF xa=xb AND ya=yb THEN

4370   =0

4380 ELSE

4390   ma=SQR(xa^2+ya^2)

4400   mb=SQR(xb^2+yb^2)

4410   adotb=xa*xb+ya*yb

4420   IF adotb>ma*mb THEN =0

4430   ndotb=xa*yb-ya*xb

4440   =DEG(ACS(adotb/(ma*mb)))*SGN(-ndotb)

4450 ENDIF

4460

4470 DEF PROCpuff(x,y,r)

4480 LOCAL p,a,d,angle,h,xo,yo

4490 p=3*r

4500 FOR a=1 TO p

4510   angle=RND(360):h=RND(r)*2

4520   xo=SIN(RAD(angle))*h

4530   yo=COS(RAD(angle))*h

4540   d=FNnewobj(7,x+xo,y+yo,5+RND(10),0,8)

4550 NEXT

4560 PROCincreasescore(p)

4570 ENDPROC

4580

4590 DEF FNintersect

4600 LOCAL xa,ya,xb,yb,xg,yg,xn,yn,ndotb,ndota,t,xd,yd

4610 REM Calculate vectors alpha, beta & gamma

4620 xa=ix{(1)}.x-ix{(0)}.x:REM alpha

4630 ya=ix{(1)}.y-ix{(0)}.y

4640 xb=ix{(2)}.x-ix{(0)}.x:REM beta

4650 yb=ix{(2)}.y-ix{(0)}.y

4660 xg=ix{(3)}.x-ix{(2)}.x:REM gamma

4670 yg=ix{(3)}.y-ix{(2)}.y

4680 xn=-yg:yn=xg:REM Normal of gamma

4690 ndotb=xn*xb+yn*yb

4700 REM Lines parallel (never intersect)?

4710 IF ndotb=0 THEN =FALSE

4720 ndota=xn*xa+yn*ya

4730 t=ndotb/ndota

4740 REM Intersect is within first line segment?

4750 IF t<0 OR t>1 THEN =FALSE

4760 REM Calculate intersect point

4770 ix{(4)}.x=ix{(0)}.x+t*xa

4780 ix{(4)}.y=ix{(0)}.y+t*ya

4790 REM Intersect is within second line segment?

4800 xd=ix{(4)}.x-ix{(2)}.x:REM delta

4810 yd=ix{(4)}.y-ix{(2)}.y

4820 IF xd*xg+yd*yg<0 OR (xd^2+yd^2)>(xg^2+yg^2) THEN =FALSE

4830 =TRUE

4840

4850 DEF PROCincreasescore(v)

4860 score+=v

4870 IF score>level*1000 THEN

4880   level+=1:probofnew+=1

4890   IF level<10 AND (level DIV 2)=0 THEN

4900     bandyyvel-=2

4910   ENDIF

4920 ENDIF

4930 ENDPROC

Dragonfly

Dragonfly: Code explained...

 

  60 MAXNODES=32

  70 DIM node(MAXNODES,3):FOR a=0 TO 26:READ node(a,0),node(a,1):NEXT

 

In previous programs we've stored the details of line-drawn graphics in an array called 'shape'. This program differs slightly in two respects.

    a) Specific routines draw specific items (e.g. frogs, lily pads), but refer to the same array.

    b) A separate routine rotates each 'node' (corner/point) before drawing, and stores the result in the same array in columns 2 and 3.

 

  80 FOR a=0 TO 4

  90   r=RND(20)/5+16:REM Randomly irregular pentagon rock

 100   node(a+27,0)=SIN(RAD(a*72))*r

 110   node(a+27,1)=COS(RAD(a*72))*r

 120 NEXT

 130 node(32,0)=node(27,0):node(32,1)=node(27,1)

 

Pentagonal rocks are drawn using three triangles and an outline. They consist of an irregular pentagon, defined above. Although slightly irregular in shape (to look more natural), the pentagon must not have any concave sides because we're going to use point-within-polygon collision testing.

For convenience, this pentagon has 6 corners, the sixth being identical to the first. (Line 130)

 

 140 MAXOBJS=200

 150 DIM obj{(MAXOBJS) status,type,x,y,angle,sc,r,fc,frog,lash}

 

All objects have a 'type', 'x', 'y', 'angle', 'sc' scale and 'r' max radius. The max radius is used to check that new objects do not overlap existing objects. It's also used to test for when an object moves completely beyond the bottom border of the window and therefore needs to be destroyed.

'fc' is the flower colour/status.

Frogs have a fixed scale and 'sc' is used to indicate if the tongue is sticking out.

'frog' and 'lash' are specific to frog tongues, which are separate objects (see below).

 

 180 DIM ix{(4) x,y}:REM Intersecting lines data block

 

The 'ix' array relates directly to FNintersect (the line segment intersect test).

 

 210 bandystart=1024+7*20:bandyvel=-4:bandy=bandystart:bandwidth=100

 220 probofnew=11:REM controls probability of lily pad or rock appearing

 

All objects begin life above the top border of the window. Objects are added in bands at regular intervals, dependent on the speed of the game.

 

 240 flycount=0:flytarget=10

 250 LICKSIZE=160:REM Frog tongue length

 260 evinrude=-1:dead=FALSE:evinergy=100

 

'flycount' and 'flytarget' control the number of active flies.

'LICKSIZE' defines the length of a frog's tongue.

'evinrude' contains either -1 or the row number allocated to the player's dragonfly in the 'obj' array.

'dead' indicates if Evinrude is dead (TRUE or FALSE).

'evinergy' holds the energy level of the dragonfly. Low energy makes Evinrude sluggish.

 

 420       evinax=0:evinay=0:evinvx=0:evinvy=0:evinergy=100

 

Evinrude's controls are based on acceleration. The player accelerates Evinrude in four different directions. Both acceleration and velocity have set limits, and velocity diminishes over time.

 

 740 REM Lily 0 offset,10 nodes

 750 DATA 0,-18,0,10,0,0,10,10,-10,10

 760 DATA 0,-12,14,0,-14,0,-4,21,4,21

 770 REM Frog 10 offset,6 nodes

 780 DATA -40,40,40,-40,-40,-40,40,40

 790 DATA 0,-10,0,15

 800 REM Evinrude 16 offset,6 nodes

 810 DATA 0,-50,0,20

 820 DATA -40,20,40,-20,-40,-20,40,20

 830 REM Fly 22 offset,5 nodes

 840 DATA 0,-22,0,8

 850 DATA 0,0,-15,-15,15,-15

 860 REM Rock 27 offset,6 nodes

 

As mentioned above, each object is defined by a set of points (nodes) relative to the object's centre, but how each point is used varies with each object. E.g. a node may be the start or end of a line, the corner of a triangle or the centre of a circle.

 

 880 DEF PROCprespin(tptr,n,x,y,angle,scale)

 

This routine is called by every routine that draws a complex object that can be rotated. It takes the template nodes and rotates them about the object centre, by a given angle, and stores the result in columns 2 and 3 of the template ('node') array.

 

 970 DEF PROClily(x,y,angle,scale)

1140 DEF PROCfrog(x,y,angle)

1230 DEF PROCfrog2(x,y,angle)

1280 DEF PROCevinrude(x,y,angle)

1390 DEF PROCfly(x,y,angle)

1500 DEF PROCrock(x,y,angle,scale)

1630 DEF PROCflower(x,y,a,col,scale)

1790 DEF PROCpollen(x,y)

 

All of the above routines draw a specific graphic, with the exception of PROCfrog2 which calls PROCfrog twice to create a highlighted frog.

 

1890 DEF FNnewobj(type,x,y,angle,sc,r)

2050 DEF PROCkillobj(p)

2120 DEF PROCele

 

Versions of the above have appeared in other programs and control the addition and removal of objects to/from the 'obj' array.

 

2180 DEF PROCthink

2190 LOCAL t,a,x,y,angle,tx,ty,achg,d

2200 IF evinrude>-1 tx=obj{(evinrude)}.x:ty=obj{(evinrude)}.y+10

2210 FOR t=0 TO 3

2220   FOR a=0 TO MAXOBJS

2230     IF obj{(a)}.status THEN

2240       x=obj{(a)}.x:y=obj{(a)}.y:angle=obj{(a)}.angle

2250       CASE t OF

2260         WHEN 0

 

This routine controls the behaviour of all objects. Our list of objects is processed in 4 passes, each pass relates the the graphical layer e.g. lily pads need to be drawn before flowers and frogs. Evinrude is drawn last.

 

2270           CASE obj{(a)}.type OF

2280             WHEN 1

2290               REM Rocks (may collide with dragonfly)

2300               obj{(a)}.y+=bandyvel

2310               PROCrock(x,y,angle,obj{(a)}.sc)

2320               IF evinrude>-1 THEN

2330                 IF FNinpolygon(tx,ty) dead=TRUE

2340               ENDIF

 

When processing rocks, we simply move them down the window and test for collision with Evinrude.

 

2350             WHEN 2

2360               REM Lilies

2370               obj{(a)}.y+=bandyvel

2380               PROClily(x,y,angle,obj{(a)}.sc)

 

Lilies are non-interactive and just need to be moved downstream.

 

2420             WHEN 3

2430               REM Flowers may create a puff of pollen when touched

2440               obj{(a)}.y+=bandyvel

2450               IF evinrude>-1 AND obj{(a)}.fc=7 THEN

2460                 IF SQR((tx-x)^2+(ty-y)^2)<obj{(a)}.sc*8 THEN

2470                   obj{(a)}.fc=4:PROCpuff(x,y,obj{(a)}.sc*8)

2480                 ENDIF

2490               ENDIF

2500               PROCflower(x,y,angle,obj{(a)}.fc,obj{(a)}.sc)

 

Flowers create a puff of pollen when visited by Evinrude, changing the flower's colour scheme.

 

2510             WHEN 4

2520               REM Frogs turn towards dragonfly and flick tongue when close

2530               obj{(a)}.y+=bandyvel

2540               IF evinrude>-1 AND NOT dead THEN

2550                 achg=FNanglebetween(tx-x,ty-y,-SIN(RAD(angle)),COS(RAD(angle)))

2560                 IF ABS(achg)>5 achg=5*SGN(achg)

2570                 angle+=achg

2580                 obj{(a)}.angle=angle

2590                 IF obj{(a)}.sc=0 AND ABS(achg)<3 THEN

2600                   d=SQR((tx-x)^2+(ty-y)^2)

2610                   IF d<LICKSIZE+40 PROCtongue(a,x,y,angle)

2620                 ENDIF

2630               ENDIF

2640               PROCfrog2(x,y,angle)

 

Frogs always turn towards Evinrude. If Evinrude is within range and within a given angle (and the frog's tongue isn't already sticking out) then a fixed length tongue is created as a separate, temporary object.

 

2650             WHEN 8

2660               REM Frog tongues are processed separately

2670               obj{(a)}.y+=bandyvel

2680               obj{(a)}.fc-=1:REM Tongue countdown

2690               IF obj{(a)}.fc<=0 THEN

2700                 IF obj{(obj{(a)}.frog)}.status THEN

2710                   obj{(obj{(a)}.frog)}.sc=0:REM Update frog about tongue (in)

2720                 ENDIF

2730                 IF obj{(a)}.lash AND evinrude>-1 THEN

2740                   obj{(evinrude)}.x=x:REM Draw Evinrude into Frog's mouth

2750                   obj{(evinrude)}.y=y

2760                   tx=obj{(evinrude)}.x:ty=obj{(evinrude)}.y+10

2770                 ENDIF

2780                 PROCkillobj(a):REM Tongue withdraws

2790               ELSE

2800                 VDU 23,23,3;0;0;0;:GCOL 0,1

2810                 LINE x,y,x-SIN(RAD(angle))*LICKSIZE,y+COS(RAD(angle))*LICKSIZE

2820               ENDIF

 

Frog's tongues are controlled independently of the 'parent' frog, but maintain a link to the frog. The tongue sticks out for a limited period of time. When it disappears, and if Evinrude has been struck, then (a dead) Evinrude is repositioned at the base of the tongue. The frog's tongue status 'sc' is changed so the frog can stick its tongue out again, if necessary.

 

2860             WHEN 5

2870               REM Flies (may collide with dragonfly)

2880               x+=SIN(RAD(obj{(a)}.angle))*bandyvel*2

2890               y-=COS(RAD(obj{(a)}.angle))*bandyvel*2

2900               obj{(a)}.x=x

2910               obj{(a)}.y=y

2920               PROCfly(x,y,angle)

2930               IF evinrude>-1 THEN

2940                 IF SQR((tx-x)^2+(ty-y)^2)<30 THEN

2950                   PROCkillobj(a):evinergy+=10:PROCincreasescore(5)

2960                 ENDIF

2970               ENDIF

 

Flies are initially located above the top border of the window, but unlike other objects they have unique x & y velocity vectors that move them downstream at an angle. We test each for a collision with Evinrude using a point and radius system.

 

2980             WHEN 7

2990               REM Pollen

3000               obj{(a)}.y+=bandyvel:angle-=1

3010               IF angle=0 PROCkillobj(a) ELSE obj{(a)}.angle=angle

3020               PROCpollen(x,y)

 

Pollen must also move/scroll down the window. Particles exist for a limited, random period. The lifespan of a pollen particle is held in the 'obj' array variable 'angle'.

 

3060             WHEN 6

3070               REM Dragonfly

3080               IF dead obj{(a)}.y+=bandyvel ELSE PROCsteer

3090               x=obj{(a)}.x:y=obj{(a)}.y:angle=obj{(a)}.angle

3100               PROCevinrude(x,y,angle)

 

Evinrude is steered by the player, unless dead, in which case the dragonfly scrolls downstream with the other objects.

 

3130       IF obj{(a)}.y<-obj{(a)}.r PROCkillobj(a)

 

All objects are destroyed when they pass completely beyond the bottom border of the window.

 

3190 DEF PROCtongue(f,x,y,angle)

 

When a frog flicks its tongue, a separate object is created, but only if there is room in the 'obj' array.

The frog's tongue status is updated to indicate a sticking-out tongue.

The tongue object stores the 'obj' array row of the frog for future reference.

The life of the tongue (how long it remains sticking out) is held in 'fc'.

The routine then calls FNintersect to determine if the line segment of the tongue intersects the line segment of Evinrude's body. The result is stored in 'lash'.

 

3450 DEF PROCupstream

3460 LOCAL a,d,x,y,scale,r,angle,tx,ty

3470 bandy+=bandyvel

3480 IF bandy<bandystart-bandwidth THEN

3490   bandy+=bandwidth

 

At specific intervals, new objects are added to the 'obj' array.

 

3500   FOR a=1 TO 10:REM Max objs/attempts per band

3510     IF RND(probofnew)>10 THEN

 

'probofnew' controls the probability of an object appearing.

 

3520       REM Scale of lily or rock

3530       scale=RND(4)+3:r=scale*20

3540       x=RND(1280-r*2)+r:y=RND(bandwidth)+bandy

3550       angle=RND(360)

 

The new object (rock or lily) will be placed randomly within the current band, without overlapping the sides of the window.

 

3560       IF FNnooverlap(x,y,r) THEN

 

Nor will a new object be created if it overlaps an existing object.

 

3570         REM 66:33 chance of lily or rock

3580         IF RND(3)=1 THEN

3590           d=FNnewobj(1,x,y,angle,scale,r)

 

If a rock is created then no further action is required.

 

3600         ELSE

3610           d=FNnewobj(2,x,y,angle,scale,r)

3620           IF d>-1 THEN

3630             IF RND(10)>5 THEN

3640               scale=RND(4)+1:REM Flower

3650               IF scale>obj{(d)}.sc scale=obj{(d)}.sc

3660               r=scale*20

3670               d=FNnewobj(3,x,y,RND(360),scale,r):IF d<>-1 obj{(d)}.fc=7

3680             ELSE

3690               IF RND(2)=1 d=FNnewobj(4,x,y,RND(360),0,60):REM Frog

3700             ENDIF

3710           ENDIF

 

If a lily is created then we must decide if it's empty, contains a flower or harbours a frog.

 

3770 IF flycount<flytarget AND RND(30)=1 THEN

3780   x=RND(1280):y=1050

3790   tx=RND(1000)+140:ty=RND(400)

3800   a=FNanglebetween(tx-x,ty-y,0,1)

3810   d=FNnewobj(5,x,y,a,0,22)

3820   IF d<>-1 flycount+=1

 

Flies are also created by the same routine. The flight path of a fly takes it through the bottom half of the window.

 

3860 DEF FNnooverlap(x,y,r)

 

This routine returns FALSE if any other object exists that overlaps the circle centred on x,y with radius 'r'.

 

3980 DEF PROCsteer

 

As described above, Evinrude's controls are based on acceleration along the x-axis and the y-axis.

 

4210 DEF FNinpolygon(x,y)

4220 LOCAL a,inside,xe,ye,xp,yp

4230 a=27:inside=TRUE

4240 REPEAT

4250   xe=x-node(a,2)

4260   ye=y-node(a,3)

4270   xp=node(a,3)-node(a+1,3)

4280   yp=node(a+1,2)-node(a,2)

4290   IF (xe*xp+ye*yp)>0 inside=FALSE

4300   a+=1

4310 UNTIL a>31 OR NOT inside

4320 =inside

 

This routine is specific to the five-sided rock template held in the 'node' array. The Vector Demo shows how we can determine whether a point lies on the left or right hand side of a line (depending on the direction of the line/vector). The pentagon is defined (and drawn) clockwise using nodes a to b, b to c, c to d, d to e, and e to f (where f=a). If a point lies on the right hand side of all five lines then the point lies inside the pentagon and the routine returns TRUE.

 

4340 DEF FNanglebetween(xa,ya,xb,yb)

4350 LOCAL ma,mb,adotb,ndotb

4360 IF xa=xb AND ya=yb THEN

4370   =0

4380 ELSE

4390   ma=SQR(xa^2+ya^2)

4400   mb=SQR(xb^2+yb^2)

4410   adotb=xa*xb+ya*yb

4420   IF adotb>ma*mb THEN =0

4430   ndotb=xa*yb-ya*xb

4440   =DEG(ACS(adotb/(ma*mb)))*SGN(-ndotb)

4450 ENDIF

 

This routine makes use of the formula for calculating the angle between two vectors (including whether a point is on the left or right of a line) to return a value between -180 and +180 degrees.

 

4470 DEF PROCpuff(x,y,r)

 

Creates a number of short-lived pollen particles. The number of particles depends on flower size.

 

4590 DEF FNintersect

 

This function was lifted directly from the Intersecting Lines program and is used to determine whether a frog's tongue intersects the line of the dragonfly's body.

 

4850 DEF PROCincreasescore(v)

4860 score+=v

4870 IF score>level*1000 THEN

4880   level+=1:probofnew+=1

4890   IF level<10 AND (level DIV 2)=0 THEN

4900     bandyyvel-=2

4910   ENDIF

4920 ENDIF

4930 ENDPROC

 

As the score increases, the probability of lilies and rocks appearing increases (similar to the method used to control the appearance of traffic in HopSquash).

Arrow black large Arrow black large