[Back to GRAPHICS SWAG index] [Back to Main SWAG index] [Original]
Program BBBBB; {yep, B's}
{Cheap thrills from the WookieWare Home Defense Series}
{Ron Nossaman August 1995}
{Critters follow each other about in a controlled but random appearing
manner. Periodically, the big'un takes off in a random direction and
the rest of the swarm straggles after her. This started out as an
experiment in orbital mechanics but typically collapsed into something
even less useful but a lot more fun. Sort of 'a life simulation for
the easily impressed'. Enjoy }
uses crt,graph,
bgiDRIV; { Wherever your have the BGI drivers compiled }
const radcon:real=0.017453292; {radian/degree conversion value}
totalbodies=22; {whatever you think looks right}
speedfactor:real=30; {lower= more frantic, higher= more sedate}
type debris=record
mass,dir,speed,x,y:real; {stuff swarms are made of}
oldx,oldy:integer;
end;
var b:array[1..totalbodies]of debris;
grdriver,grmode,errcode: integer;
ch:char;
i,countdown,walls,delayfactor:integer;
stampede:boolean;
bait:debris;
function theta(p1x,p1y,p2x,p2y:real):real;
{Given two sets of point coordinates, returns the angle
between them in degrees. I've found this to be very handy}
var t,lx,ly:real;
begin
lx:=p2x-p1x; ly:=p2y-p1y;
if((lx=0)and(ly=0))then theta:=0 else
begin
if lx=0 then t:=arctan(ly/0.0000000001)/radcon else
t:=arctan(ly/lx)/radcon; {divide by zero duck}
if lx<0 then t:=t+180; {vector adjustments}
if((lx>=0)and(ly<0))then t:=t+360; {back into range}
theta:=abs(t);
end;
end;
function hypotenuse(x1,y1,x2,y2:real):real;
{just what it says}
var h1,h2:real;
begin
hypotenuse:=sqrt(sqr(abs(x1-x2))+sqr(abs(y1-y2)));
end;
procedure init; {Gentlemen, start your b's}
var i:integer;
g:real;
begin
for i:=1 to totalbodies do
begin
b[i].x:=random(600)+20;
b[i].y:=random(440)+20;
b[i].dir:=random(359)+1;
b[i].speed:=(random(100)+50)/speedfactor;
b[i].mass:=random(50)+100;
bait.x:=random(600)+20;
bait.y:=random(440)+20;
bait.mass:=100;
end;
delayfactor:=round(500/b[1].speed);
end;
procedure orbit(a:integer);
var i,a2,min,xmax,ymax:integer;
t,m1,m2,ax,ay,newdir,rate:real;
begin
if a=1 then a2:=totalbodies else a2:=a-1; {a=1 is the queen}
if (stampede and (a=1)) then
begin {beeline toward bait}
t:=theta(b[a].x,b[a].y,bait.x,bait.y); {target vector}
m2:=hypotenuse(b[a].x,b[a].y,bait.x,bait.y); {distance}
m1:=bait.mass; {attraction factor}
rate:=10; {queen's cornering ability}
end else {smooths abrupt directional changes}
begin
t:=theta(b[a].x,b[a].y,b[a2].x,b[a2].y); {follow last in line instead}
m2:=hypotenuse(b[a].x,b[a].y,b[a2].x,b[a2].y);
m1:=b[a2].mass;
rate:=4;
end;
ax:=b[a].x+(m1*cos(b[a].dir*radcon))+(m2*cos(t*radcon)); {new position}
ay:=b[a].y+(m1*sin(b[a].dir*radcon))+(m2*sin(t*radcon));
newdir:=theta(b[a].x,b[a].y,ax,ay); {resulting direction deflection}
{this is not a very efficient way of doing this, but when I got
it to do what I wanted, I quit dinking with it}
if a=1 then
begin
if abs(newdir-b[a].dir)>=180 then
begin
if newdir>b[a].dir then newdir:=newdir-360
else newdir:=newdir+360;
end; {smooth direction change rate}
if newdir>b[a].dir then b[a].dir:=b[a].dir+((newdir-b[a].dir)/rate);
if newdir<b[a].dir then b[a].dir:=b[a].dir-((b[a].dir-newdir)/rate);
if round(newdir)=round(b[a].dir)
then b[a].dir:=b[a].dir+random(10)-5;
end else b[a].dir:=newdir;
if b[a].dir>359 then b[a].dir:=b[a].dir-360; {fix direction overflow}
if b[a].dir<0 then b[a].dir:=b[a].dir+360;
b[a].x:=b[a].x+(b[a].speed*cos(b[a].dir*radcon));
b[a].y:=b[a].y+(b[a].speed*sin(b[a].dir*radcon));
if b[a].x<3 then b[a].x:=3;
if b[a].x>637 then b[a].x:=637;
if b[a].y<3 then b[a].y:=3;
if b[a].y>477 then b[a].y:=477;
if(b[a].x=637)or(b[a].x=3)or(b[a].y=477)or(b[a].y=3) then
begin
b[a].speed:=(random(100)+50)/speedfactor;
if a=1 then delayfactor:=round(500/b[1].speed);
end;
if (b[a].oldx<>round(b[a].x))or(b[a].oldy<>round(b[a].y)) then
begin
setcolor(black);
circle(b[a].oldx,b[a].oldy,1); {erase old position}
if a=1 then circle(b[a].oldx,b[a].oldy,2);
setcolor(white);
circle(round(b[a].x),round(b[a].y),1); {draw new position}
if a=1 then
begin
setcolor(lightgray);
circle(round(b[a].x),round(b[a].y),2); {queen's bigger}
end;
end;
b[a].oldx:=round(b[a].x);
b[a].oldy:=round(b[a].y);
end;
procedure Abort(Msg : string);
begin
Writeln(Msg, ': ', GraphErrorMsg(GraphResult));
Halt(1);
end;
Begin
if RegisterBGIdriver(@EGAVGADriverProc)<0 then abort('EGA/VGA');
countdown:=100;
stampede:=false;
randomize;
ch:=#0;
grDriver := Detect;
InitGraph(grDriver,grmode,'');
setgraphmode(2);
ErrCode := GraphResult;
if ErrCode <> grOk then
begin
CloseGraph;
Writeln('Graphics error:', GraphErrorMsg(ErrCode));
exit;
end;
init;
while keypressed do ch:=readkey;
repeat
for i:=1 to totalbodies do orbit(i);
dec(countdown);
if countdown<0 then
begin
stampede:=not stampede;
if stampede then
begin
{de-bracket to c-d-bait}
{ setcolor(black);
circle(round(bait.x),round(bait.y),2);}
walls:=random(4);
countdown:=random(delayfactor)+delayfactor*2;
case walls of {the bait always goes on a wall because}
0:begin {it makes the migrations more dramatic}
bait.x:=20; {and I like the effect. that's why}
bait.y:=random(480);
end;
1:begin
bait.x:=619;
bait.y:=random(480);
end;
2:begin
bait.y:=20;
bait.x:=random(640);
end;
3:begin
bait.y:=459;
bait.x:=random(640);
end;
end;
{de-bracket to c-d-bait}
{ setcolor(lightblue);
circle(round(bait.x),round(bait.y),2);}
end else countdown:=random(delayfactor)+delayfactor*2;
end;
if keypressed then ch:=readkey;
until ch=#27;
closegraph;
end.
[Back to GRAPHICS SWAG index] [Back to Main SWAG index] [Original]