Browse Source

Added billiards banana.

Mark Sibly 8 years ago
parent
commit
15ae0bae8b
31 changed files with 5825 additions and 0 deletions
  1. 1021 0
      bananas/billiards/PoolMod/Ball2D.monkey2
  2. 558 0
      bananas/billiards/PoolMod/BallAnimation.monkey2
  3. 398 0
      bananas/billiards/PoolMod/Common.monkey2
  4. 272 0
      bananas/billiards/PoolMod/Engine.monkey2
  5. 229 0
      bananas/billiards/PoolMod/FileSystem.monkey2
  6. 465 0
      bananas/billiards/PoolMod/Font.monkey2
  7. 717 0
      bananas/billiards/PoolMod/Gui.monkey2
  8. 226 0
      bananas/billiards/PoolMod/Media.monkey2
  9. 162 0
      bananas/billiards/PoolMod/PlayerRecords.monkey2
  10. 14 0
      bananas/billiards/PoolMod/PoolMod.monkey2
  11. BIN
      bananas/billiards/PoolMod/data/applaused.ogg
  12. BIN
      bananas/billiards/PoolMod/data/ball.png
  13. BIN
      bananas/billiards/PoolMod/data/bank01.ogg
  14. BIN
      bananas/billiards/PoolMod/data/bank02.ogg
  15. BIN
      bananas/billiards/PoolMod/data/beep.ogg
  16. BIN
      bananas/billiards/PoolMod/data/beep2.ogg
  17. BIN
      bananas/billiards/PoolMod/data/button.png
  18. BIN
      bananas/billiards/PoolMod/data/completion.ogg
  19. BIN
      bananas/billiards/PoolMod/data/fail.ogg
  20. BIN
      bananas/billiards/PoolMod/data/frame.png
  21. BIN
      bananas/billiards/PoolMod/data/frame3.png
  22. BIN
      bananas/billiards/PoolMod/data/hit03.ogg
  23. BIN
      bananas/billiards/PoolMod/data/hit04.ogg
  24. BIN
      bananas/billiards/PoolMod/data/hit05.ogg
  25. BIN
      bananas/billiards/PoolMod/data/hit06.ogg
  26. BIN
      bananas/billiards/PoolMod/data/pocket1.ogg
  27. BIN
      bananas/billiards/PoolMod/data/poolAtlasb.png
  28. BIN
      bananas/billiards/PoolMod/data/rackup.ogg
  29. BIN
      bananas/billiards/PoolMod/data/railhit.ogg
  30. BIN
      bananas/billiards/PoolMod/data/shoot.ogg
  31. 1763 0
      bananas/billiards/pool.monkey2

+ 1021 - 0
bananas/billiards/PoolMod/Ball2D.monkey2

@@ -0,0 +1,1021 @@
+
+#Import "PoolMod"
+
+Class VectorObject
+	
+	Global Gravityx:Float
+	Global Gravityy:Float
+	Global ApplicableGravityx:Float
+	Global ApplicableGravityy:Float
+	Global SurfaceFriction:Float
+	Global ApplicableSurfaceFriction:Float
+	
+	Field image:Image
+	
+	Field P:PVector2D
+	Field V:PVector2D
+	Field D:PVector2D
+	Field cp:PVector2D
+	Field name:String=""
+	
+	Field friction:Float
+	Field bounce:Float
+	Field animation:ObjectAnimation
+	
+	Field L:Float
+	
+	Field cdx:Float
+	Field cdy:Float
+	Field tp:PVector2D
+
+	Function SetGlobalGravity(t_gx:Float,t_gy:Float)
+		Gravityx=t_gx
+		Gravityy=t_gy
+		ApplicableGravityx=t_gx
+		ApplicableGravityy=t_gy
+	End
+	
+	Function SetGlobalFriction(f:Float)
+		SurfaceFriction=f
+		ApplicableSurfaceFriction=f
+	End
+	
+	Method Magnitude:Float(vx:Float,vy:Float)
+		Return Sqrt(Self.V.x*Self.V.x+Self.V.y*Self.V.y)
+	End
+
+	Method Init(vx:Float,vy:Float)
+		Self.V.x=vx
+		Self.V.y=vy
+		Self.L=Self.Magnitude(vx,vy)
+		If(Self.L>0.0)
+			Self.D.x=Self.V.x/Self.L
+			Self.D.y=Self.V.y/Self.L
+		Else
+			Self.D.x=0.0
+			Self.D.y=0.0
+		End
+	End
+	
+	Method SetAnimation(animation:ObjectAnimation)
+		Self.animation=animation
+	End
+	
+	Field num:Int
+	Field node:List<Ball>.Node
+	
+	Method Distance:Float(b:Ball)Virtual
+		Return 0
+	End
+	
+	Method Bounce(ball:Ball,px:Float,py:Float) Abstract
+
+	Method Render(canvas:Canvas) Abstract
+
+	Method CollisionDistanceB2RW:Float(ball:Ball,x:Float,y:Float,radius:Float)
+		Self.tp = Null
+		Local vx:Float=x-ball.P.x
+		Local vy:Float=y-ball.P.y
+		Local totRadiusSq:Float=(ball.radius+radius)*(ball.radius+radius)
+		Local distSq:Float=vx*vx+vy*vy
+		If(distSq<totRadiusSq And distSq>radius*radius)
+			If((ball.P.x-x)*(0.0-ball.V.x)+(ball.P.y-y)*(0.0-ball.V.y)>0.0)
+				Local len:Float=Sqrt(distSq)
+				Self.cdx=vx/len
+				Self.cdy=vy/len
+				Self.cp.x=ball.P.x+Self.cdx*ball.radius
+				Self.cp.y=ball.P.y+Self.cdy*ball.radius
+				Self.tp=Self.cp
+				Return 0.0
+			End
+		End
+		Local vx2:Float=x-(ball.P.x+ball.V.x)
+		Local vy2:Float=y-(ball.P.y+ball.V.y)
+		Local dp:Float=vx*ball.D.x+vy*ball.D.y
+		If(dp>0.0)
+			Local tvx:Float=dp*ball.D.x
+			Local tvy:Float=dp*ball.D.y
+			Local x1:Float=ball.P.x+tvx
+			Local y1:Float=ball.P.y+tvy
+			tvx=x-x1
+			tvy=y-y1
+			Local llSq:Float=tvx*tvx+tvy*tvy
+			Local maxDist:Float=radius+ball.radius
+			If(llSq<=maxDist*maxDist)
+				Local c:Float=ball.radius+radius
+				Local b:Float=Sqrt(llSq)
+				Local a:Float=Sqrt(c*c-b*b)
+				Local x2:Float=x1-ball.D.x*a
+				Local y2:Float=y1-ball.D.y*a
+				Local rvx:Float=x2-ball.P.x
+				Local rvy:Float=y2-ball.P.y
+				Local distSq2:Float=rvx*rvx+rvy*rvy
+				If(distSq2<=ball.L*ball.L And vx*rvx+vy*rvy>=0.0)
+					vx=x-(ball.P.x+rvx)
+					vy=y-(ball.P.y+rvy)
+					If((c)<>0.0)
+						Self.cdx=vx/c
+						Self.cdy=vy/c
+					Else
+						Self.cdx=0.0
+						Self.cdy=0.0
+					End
+					Self.cp.x=ball.P.x+rvx+Self.cdx*ball.radius
+					Self.cp.y=ball.P.y+rvy+Self.cdy*ball.radius
+					Self.tp=Self.cp
+					Local t_:Float=Sqrt(distSq2)/ball.L
+					Return t_
+				End
+			End
+		End
+		Return INVALID_DISTANCE
+		
+	End Method
+	
+	Method Bounce2Fixed(ball:Ball,dx:Float,dy:Float)
+		Local dp:Float=ball.V.x*dx+ball.V.y*dy
+		Local vx1:Float=-dp*dx
+		Local vy1:Float=-dp*dy
+		dp=ball.V.x*dy-ball.V.y*dx
+		Local vx2:Float=dp*dy
+		Local vy2:Float=-dp*dx
+		Local vx:Float=vx1+vx2
+		Local vy:Float=vy1+vy2
+		ball.Init(vx,vy)
+		dp=ball.tvx*dx+ball.tvy*dy
+		vx1=-dp*dx
+		vy1=-dp*dy
+		dp=ball.tvx*dy-ball.tvy*dx
+		vx2=dp*dy
+		vy2=-dp*dx
+		ball.tvx=vx1+vx2
+		ball.tvy=vy1+vy2
+		Local n:Float=ball.tvx+ball.V.x
+		If(n<5.0) n=0.0
+		If(n>=5.0) n=1.0
+		media.PlayRailCol(n)
+	End
+	
+	Method CollisionDistance2Ghost:Float(ball:GhostBall) Abstract
+
+	Method Distance2Ghost:Float(ghost:GhostBall,x:Float,y:Float,radius:Float)
+		Local vx:Float=x-ghost.position.x
+		Local vy:Float=y-ghost.position.y
+		Local dp:Float=vx*ghost.dy-vy*ghost.dx
+		Local c:Float=ghost.radius+radius
+		If Abs(dp) < c
+			Local dp2:Float=vx*ghost.dx+vy*ghost.dy
+			If dp2 > 0.0
+				dp2-=Sqrt(c*c-dp*dp)
+				vx=ghost.position.x+ghost.dx*dp2-x
+				vy=ghost.position.y+ghost.dy*dp2-y
+				dp=Sqrt(vx*vx+vy*vy)
+				ghost.cn.x=vx/dp
+				ghost.cn.y=vy/dp
+				Return dp2
+			End
+		End
+		Return INVALID_DISTANCE
+	End
+	
+End Class
+
+
+'************************************************************************
+'
+'	Movable vector ball
+'
+'************************************************************************
+Class Ball Extends VectorObject
+	Field tv:PVector2D
+	
+	Field radius:Float
+	Field mass:Float
+	Field displayFloat:Bool
+
+	Field tvx:Float
+	Field tvy:Float
+
+	Method New()
+		P=New PVector2D()
+		V=New PVector2D()
+		D=New PVector2D()
+		tv=New PVector2D()
+		cp=New PVector2D()
+		name="Ball"
+	End
+	
+	Method New(x:Float,y:Float,radius:Float,vx:Float,vy:Float,animation:ObjectAnimation,frictn:Float,bounse:Float,mass:Float,num:Int,displayFloat:Bool)
+		P=New PVector2D()
+		V=New PVector2D()
+		D=New PVector2D()
+		tv=New PVector2D()
+		cp=New PVector2D()
+		name="Ball"
+		P.x=x
+		P.y=y
+		Self.radius=radius
+		Init(vx,vy)
+		friction=frictn
+		bounce=bounse
+		Self.mass=mass
+		Self.num=num
+		Self.displayFloat=displayFloat
+		Self.animation=animation
+	End
+	
+	Method SetPosition(x:Float,y:Float)
+		P.x=x
+		P.y=y
+	End
+	
+	Method ResetAnimation()
+		animation.Reset()
+	End
+	
+	Method updateIn(timeFrame:Float)
+		V.x+=VectorObject.ApplicableGravityx
+		V.y+=VectorObject.ApplicableGravityy
+		V.x*=VectorObject.ApplicableSurfaceFriction
+		V.y*=VectorObject.ApplicableSurfaceFriction
+		V.x*=timeFrame
+		V.y*=timeFrame
+		Init(V.x,V.y)
+	End
+	Method IsMoving:Bool()
+		Return ((V.x)<>0.0) Or ((V.y)<>0.0)
+	End
+	Method MovingToBall:Bool(ball:Ball)
+		Return (ball.P.x-P.x)*(V.x-ball.V.x)+(ball.P.y-P.y)*(V.y-ball.V.y)>0.0
+	End
+
+	Method Distance:Float(ball:Ball) Override
+		Local vx:Float=ball.P.x-P.x
+		Local vy:Float=ball.P.y-P.y
+		
+		If(vx*vx+vy*vy<(radius+ball.radius)*(radius+ball.radius))
+			If(MovingToBall(ball))
+				Local len:Float=Sqrt(vx*vx+vy*vy)
+				cdx=vx/len
+				cdy=vy/len
+				Return 0.0
+			End
+		End
+		If(-vx*ball.V.x-vy*ball.V.y<0.0)
+			Return INVALID_DISTANCE
+		End
+		Local vx1:Float=V.x-ball.V.x
+		Local vy1:Float=V.y-ball.V.y
+		Local totalRadius:Float=radius+ball.radius
+		Local l1:Float=Sqrt(vx1*vx1+vy1*vy1)
+		Local dx1:Float=vx1/l1
+		Local dy1:Float=vy1/l1
+		Local dp:Float=vx*dx1+vy*dy1
+		Local vx2:Float=dp*dx1
+		Local vy2:Float=dp*dy1
+		Local lengt:Float=Sqrt(Pow(ball.P.x-(P.x+vx2),2.0)+Pow(ball.P.y-(P.y+vy2),2.0))
+		Local diff:Float=totalRadius-lengt
+		tp = Null
+		If(diff>0.0)
+			Local moveBack:Float=Sqrt(totalRadius*totalRadius-lengt*lengt)
+			Local vx4:Float=vx2-moveBack*dx1
+			Local vy4:Float=vy2-moveBack*dy1
+			Local l4:Float=Sqrt(vx4*vx4+vy4*vy4)
+			If(l4<=l1 And vx4*V.x+vy4*V.y>=0.0)
+				Local dist:Float=l4/l1
+				Local x1:Float=ball.P.x+ball.V.x*dist
+				Local y1:Float=ball.P.y+ball.V.y*dist
+				Local x2:Float=P.x+V.x*dist
+				Local y2:Float=P.y+V.y*dist
+				Local vx3:Float=x2-x1
+				Local vy3:Float=y2-y1
+				l1=Sqrt(vx3*vx3+vy3*vy3)
+				If((l1)<>0.0)
+					cdx=vx3/l1
+					cdy=vy3/l1
+				Else
+					cdx=0.0
+					cdy=0.0
+				End
+				cp.x=x1+cdx*ball.radius
+				cp.y=y1+cdy*ball.radius
+				tp=cp
+				Return dist
+			End
+		End
+		Return INVALID_DISTANCE
+	End
+
+	Method Advance(distance:Float)
+		Local vx:Float=V.x*distance
+		Local vy:Float=V.y*distance
+		If(vx=0.0 And vy=0.0)
+			Return
+		End
+		V.x-=vx
+		V.y-=vy
+		P.x+=vx
+		P.y+=vy
+		tvx+=vx
+		tvy+=vy
+		L-=Sqrt(vx*vx+vy*vy)
+	End
+	Method updateOut(timeFrame:Float)
+		V.x=tvx
+		V.y=tvy
+		If(cast<ABall3D>(animation)<>Null)
+			Cast<ABall3D>(animation).Rotate(tvx,tvy)
+		End
+		tvx=0.0
+		tvy=0.0
+		V.x/=timeFrame
+		V.y/=timeFrame
+		If(Abs(V.x)<0.1 And Abs(V.y)<0.1)
+			V.x=0.0
+			V.y=0.0
+		End
+		Init(V.x,V.y)
+	End
+	
+	Method Render(canvas:Canvas) Override
+		If((animation)<>Null)
+			animation.Render(canvas)
+		End
+		If(displayFloat)
+			canvas.DrawText(num,P.x,P.y,0.0,0.0)
+		End
+	End
+	
+	Method BounceB2B(ball:Ball,dx:Float,dy:Float)
+		Local vx1:Float=ball.tvx+ball.V.x
+		Local vy1:Float=ball.tvy+ball.V.y
+		Local dp:Float=V.x*dx+V.y*dy
+		Local a1:Float=dp*dx
+		Local b1:Float=dp*dy
+		dp=V.x*dy-V.y*dx
+		Local a2:Float=dp*dy
+		Local b2:Float=dp*-dx
+		dp=ball.V.x*dx+ball.V.y*dy
+		Local a21:Float=dp*dx
+		Local b21:Float=dp*dy
+		dp=ball.V.x*dy-ball.V.y*dx
+		Local a22:Float=dp*dy
+		Local b22:Float=dp*-dx
+		Local t_P:Float=mass*a1+ball.mass*a21
+		Local t_Vn:Float=a1-a21
+		Local v2fx:Float=(t_P+t_Vn*mass)/(mass+ball.mass)
+		Local v1fx:Float=v2fx-t_Vn
+		t_P=mass*b1+ball.mass*b21
+		t_Vn=b1-b21
+		Local v2fy:Float=(t_P+t_Vn*mass)/(mass+ball.mass)
+		Local v1fy:Float=v2fy-t_Vn
+		V.x=friction*ball.friction*a2+bounce*ball.bounce*v1fx
+		V.y=friction*ball.friction*b2+bounce*ball.bounce*v1fy
+		ball.V.x=friction*ball.friction*a22+bounce*ball.bounce*v2fx
+		ball.V.y=friction*ball.friction*b22+bounce*ball.bounce*v2fy
+		Init(V.x,V.y)
+		ball.Init(ball.V.x,ball.V.y)
+		dp=tvx*dx+tvy*dy
+		a1=dp*dx
+		b1=dp*dy
+		dp=tvx*dy-tvy*dx
+		a2=dp*dy
+		b2=dp*-dx
+		dp=ball.tvx*dx+ball.tvy*dy
+		a21=dp*dx
+		b21=dp*dy
+		dp=ball.tvx*dy-ball.tvy*dx
+		a22=dp*dy
+		b22=dp*-dx
+		t_P=mass*a1+ball.mass*a21
+		t_Vn=a1-a21
+		v2fx=(t_P+t_Vn*mass)/(mass+ball.mass)
+		v1fx=v2fx-t_Vn
+		t_P=mass*b1+ball.mass*b21
+		t_Vn=b1-b21
+		v2fy=(t_P+t_Vn*mass)/(mass+ball.mass)
+		v1fy=v2fy-t_Vn
+		tvx=friction*ball.friction*a2+bounce*ball.bounce*v1fx
+		tvy=friction*ball.friction*b2+bounce*ball.bounce*v1fy
+		ball.tvx=friction*ball.friction*a22+bounce*ball.bounce*v2fx
+		ball.tvy=friction*ball.friction*b22+bounce*ball.bounce*v2fy
+		vx1-=ball.V.x+ball.tvx
+		vy1-=ball.V.y+ball.tvy
+		dp=Sqrt(vx1*vx1+vy1*vy1)
+		If(dp>20.0)
+			media.PlayBallCol(3)
+		Else
+			If(dp>10.0)
+				media.PlayBallCol(2)
+			Else
+				If(dp>2.0)
+					media.PlayBallCol(1)
+				Else
+					media.PlayBallCol(0)
+				End
+			End
+		End
+	End
+	
+	Method Bounce(ball:Ball,dx:Float,dy:Float) Override
+		BounceB2B(ball,dx,dy)
+	End
+	
+	Method CollisionDistance2Ghost:Float(ball:GhostBall) Override
+		If num=16 Return INVALID_DISTANCE
+		Return Distance2Ghost(ball,P.x,P.y,radius)
+	End
+End
+
+Class LineWall Extends VectorObject
+	
+	Method New()
+		Self.P=New PVector2D()
+		Self.V=New PVector2D()
+		Self.D=New PVector2D()
+		Self.cp=New PVector2D()
+		Self.name="Straight wall"
+	End
+
+	Method New(x1:Float,y1:Float,x2:Float,y2:Float,animation:ObjectAnimation,friction:Float,bounce:Float)
+		Self.P=New PVector2D()
+		Self.V=New PVector2D()
+		Self.D=New PVector2D()
+		Self.cp=New PVector2D()
+		Self.name="Straight wall"
+		Self.P.x=x1
+		Self.P.y=y1
+		Self.Init(x2-x1,y2-y1)
+		Self.friction=friction
+		Self.bounce=bounce
+		Self.animation=animation
+	End
+	Method Distance:Float(ball:Ball) Override
+		Self.tp = null
+		Local a:Float=ball.P.x-Self.P.x
+		Local b:Float=ball.P.y-Self.P.y
+		Local a1:Float=ball.P.x+ball.V.x-Self.P.x
+		Local b1:Float=ball.P.y+ball.V.y-Self.P.y
+		Local d1:Float=a*Self.D.y-b*Self.D.x
+		Local d2:Float=a1*Self.D.y-b1*Self.D.x
+		If(Abs(d1)<ball.radius)
+			Local vx:Float=ball.P.x-(Self.P.x+Self.V.x)
+			Local vy:Float=ball.P.y-(Self.P.y+Self.V.y)
+			If(vx*Self.V.x+vy*Self.V.y<=0.0 And a*Self.V.x+b*Self.V.y>=0.0)
+				If(d1>0.0 And d1>d2)
+					Self.cdx=-Self.D.y
+					Self.cdy=Self.D.x
+					Return 0.0
+				Else
+					If(d1<0.0 And d1<d2)
+						Self.cdx=Self.D.y
+						Self.cdy=-Self.D.x
+						Return 0.0
+					End
+				End
+			End
+		End
+		Local t:Float=0.0
+		If(d1>0.0)
+			t=(ball.radius-d1)/(d2-d1)
+		Else
+			If(d1<0.0)
+				t=(ball.radius+d1)/(d1-d2)
+			End
+		End
+		If(d1>0.0 And d2>=d1 Or d1<0.0 And d2<=d1)
+			Local vx2:Float=ball.P.x-(Self.P.x+Self.V.x)
+			Local vy2:Float=ball.P.y-(Self.P.y+Self.V.y)
+			If(a*Self.D.x+b*Self.D.y>=0.0 And vx2*Self.D.x+vy2*Self.D.y<0.0)
+				Return INVALID_DISTANCE
+			End
+		End
+		If(t<=1.0 And t>=0.0)
+			a=ball.V.x*t
+			b=ball.V.y*t
+			Self.cp.x=ball.P.x+a
+			Self.cp.y=ball.P.y+b
+			Local vx3:Float=Self.cp.x-Self.P.x
+			Local vy3:Float=Self.cp.y-Self.P.y
+			Local dp:Float=vx3*Self.D.x+vy3*Self.D.y
+			If(dp>0.0)
+				vx3=Self.cp.x-(Self.P.x+Self.V.x)
+				vy3=Self.cp.y-(Self.P.y+Self.V.y)
+				Self.cp.x=Self.P.x+dp*Self.D.x
+				Self.cp.y=Self.P.y+dp*Self.D.y
+				dp=vx3*Self.D.x+vy3*Self.D.y
+				If(dp<0.0)
+					If(Abs(d1)<ball.radius)
+						Local diff:Float=ball.radius-Abs(d1)
+						ball.P.x+=diff*Self.D.y
+						ball.P.y-=diff*Self.D.x
+					End
+					Self.cdx=Self.D.y
+					Self.cdy=-Self.D.x
+					Self.tp=Self.cp
+					Return t
+				End
+			End
+		End
+		Local dist:Float=Self.CollisionDistanceB2RW(ball,Self.P.x+Self.V.x,Self.P.y+Self.V.y,0.0)
+		If(dist<>INVALID_DISTANCE)
+			Return dist
+		End
+		dist=Self.CollisionDistanceB2RW(ball,Self.P.x,Self.P.y,0.0)
+		If(dist=INVALID_DISTANCE)
+			Self.tp = null
+		End
+		
+		Return dist
+	End
+	Method Bounce(ball:Ball,dx:Float,dy:Float) Override
+		Self.Bounce2Fixed(ball,dx,dy)
+	End
+	Method Render(canvas:Canvas) Override
+		If((Self.animation)<>Null)
+			Self.animation.Render(canvas)
+		End
+	End
+	Method CollisionDistance2Ghost:Float(ball:GhostBall) Override
+		Self.tp = null
+		Local a:Float=ball.position.x-Self.P.x
+		Local b:Float=ball.position.y-Self.P.y
+		Local a1:Float=ball.position.x+ball.dx-Self.P.x
+		Local b1:Float=ball.position.y+ball.dy-Self.P.y
+		Local d1:Float=a*Self.D.y-b*Self.D.x
+		Local d2:Float=a1*Self.D.y-b1*Self.D.x
+		Local t:Float=0.0
+		If(d1>0.0)
+			t=(ball.radius-d1)/(d2-d1)
+		Else
+			If(d1<0.0)
+				t=(ball.radius+d1)/(d1-d2)
+			End
+		End
+		If(d1>0.0 And d2>=d1 Or d1<0.0 And d2<=d1)
+			Return INVALID_DISTANCE
+		End
+		If(t>=0.0)
+			Local px:Float=ball.position.x+ball.dx*t
+			Local py:Float=ball.position.y+ball.dy*t
+			Local vx:Float=px-Self.P.x
+			Local vy:Float=py-Self.P.y
+			Local dp:Float=vx*Self.D.x+vy*Self.D.y
+			If(dp>0.0)
+				vx=px-(Self.P.x+Self.V.x)
+				vy=py-(Self.P.y+Self.V.y)
+				dp=vx*Self.D.x+vy*Self.D.y
+				If(dp<0.0)
+					ball.cn.x=-Self.D.y
+					ball.cn.y=Self.D.x
+					Return t
+				End
+			End
+		End
+		Local dist:Float=Self.Distance2Ghost(ball,P.x+V.x,P.y+V.y,0.0)
+		Local dx1:Float=ball.cn.x
+		Local dy1:Float=ball.cn.y
+		Local dist2:Float=Self.Distance2Ghost(ball,P.x,P.y,0.0)
+		If dist2<dist
+			dist=dist2
+		Else
+			ball.cn.x=dx1
+			ball.cn.y=dy1
+		End
+		Return dist
+	End
+End
+
+'*******************************************************
+'
+'	static roundwall(circle)
+'
+'*******************************************************
+
+Class CircleWall Extends VectorObject
+
+	Field radius:Float 'circle radius
+	
+	Method New()
+		P = New PVector2D
+		cp = New PVector2D
+		name = "round wall"
+	End Method
+	
+	Method New(x:Float,y:Float,radius:Float,animation:ObjectAnimation = Null,friction:Float = 1,bounce:Float=1)
+		
+		P = New PVector2D
+		cp = New PVector2D
+		
+		name = "round wall"
+		
+		P.x = x
+		P.y = y
+		
+		Self.radius = radius
+		Self.friction = friction
+		Self.bounce = bounce
+		Self.animation = animation
+	
+	End Method
+	
+	Method SetFriction(f:Float)
+		friction = f
+	End Method
+	
+	Method Update()
+	
+	End Method
+
+	'************************************************************************
+	'
+	'	distance of ball to wall(circle)
+	'	returned values
+	' 	valid distance distance 0.0 - 1.0.
+	'
+	'************************************************************************
+	
+	Method Distance:Float(ball:Ball) Override
+		Return CollisionDistanceB2RW(ball,P.x,P.y,radius)
+	End Method
+	
+	'************************************************************************
+	'
+	'	change movement vector after collision with wal(circle)
+	'	
+	'************************************************************************
+	 
+	Method Bounce(ball:Ball,dx:Float,dy:Float) Override
+		Bounce2Fixed(ball,dx,dy)
+	End Method
+
+	Method Render(canvas:Canvas) Override
+		If animation
+			animation.Render(canvas)
+		Else
+			RenderCircle(canvas,P.x,P.y,radius)
+		Endif
+	End Method
+
+End Class
+
+Class ArcWall Extends VectorObject
+	Field pa:PVector2D
+	Field np:PVector2D
+	Field target:PVector2D
+	Field radius:Float
+	Field startAngle:Float
+	Field endAngle:Float
+	
+	Method New()
+		
+		P = New PVector2D()
+		V = New PVector2D()
+		D = New PVector2D()
+		pa = New PVector2D()
+		cp = New PVector2D()
+		np = New PVector2D()
+		name="Arc Wall"
+		
+	End Method
+	
+	Method SetAperture()
+		
+		pa.x = P.x+Cos(startAngle*ATR)*radius
+		pa.y = P.y+Sin(startAngle*ATR)*radius
+		V.x =  P.x+Cos(endAngle*ATR)*radius-pa.x
+		V.y =  P.y+Sin(endAngle*ATR)*radius-pa.y
+		Init(V.x,V.y)
+		
+	End Method
+	
+	Method New(x:Float,y:Float,radius:Float,startAngle:Float,endAngle:Float,animation:ObjectAnimation=Null,friction:Float=1.0,bounce:Float=1.0)
+		
+		P=New PVector2D()
+		V=New PVector2D()
+		D=New PVector2D()
+		pa=New PVector2D()
+		cp=New PVector2D()
+		np=New PVector2D()
+		target=New PVector2D()
+		name="Arc Wall"
+		P.x=x
+		P.y=y
+		target.x=x
+		target.y=y
+		Self.radius=radius
+		Self.startAngle=startAngle
+		Self.endAngle=endAngle
+		SetAperture()
+		Self.friction=friction
+		Self.bounce=bounce
+		Self.animation=animation
+		
+	End Method
+	
+	Method SetTarget(x:Float,y:Float)
+		
+		target.x=x
+		target.y=y
+		
+	End Method
+	
+	Method BallInside:Bool(ball:Ball)
+		
+		Local vx:Float=ball.P.x-P.x
+		Local vy:Float=ball.P.y-P.y
+		Return (vx*vx+vy*vy) < (radius * radius)
+		
+	End Method
+	
+	Method Distance:float(ball:Ball) Override
+		
+		If(ball.L = 0.0) Return INVALID_DISTANCE
+		
+		tp=Null
+		Local tcdx:Float
+		Local tcdy:Float
+		Local dist:Float=Self.CollisionDistanceB2RW(ball,P.x,P.y,radius)
+		If(dist<>INVALID_DISTANCE)
+			Local vx:Float=tp.x-pa.x
+			Local vy:Float=tp.y-pa.y
+			If(vx*Self.V.y-vy*Self.V.x>=0.0) Return dist
+			Self.tp=Null
+			dist=INVALID_DISTANCE
+		Endif
+		Local vx2:Float=P.x-ball.P.x
+		Local vy2:Float=P.y-ball.P.y
+		Local vx1:Float=-vx2
+		Local vy1:Float=-vy2
+		Local totRadius:Float=radius-ball.radius
+		Local distSq:Float=vx2*vx2+vy2*vy2
+		Local totRadSq:Float=totRadius*totRadius
+		If(distSq>totRadSq  And  distSq < radius*radius)
+			Local dst:Float=Sqrt(distSq)
+			if(dst>0.0)
+				cdx=vx1/dst
+				cdy=vy1/dst
+			Else
+				cdx=ball.D.x
+				cdy=ball.D.y
+			Endif
+			vx1=cdx*radius
+			vy1=cdy*radius
+			cp.x=P.x+vx1
+			cp.y=P.y+vy1
+			If(vx1*D.y-vy1*D.x>0.0  And  vx1*ball.D.x+vy1*ball.D.y>=0.0)
+				tp=cp
+				Return 0.0
+			Endif
+		Endif
+		Local dp:Float=vx2*ball.D.y-vy2*ball.D.x
+		If(Abs(dp)<radius)
+			vx2=dp*ball.D.y
+			vy2=dp*-ball.D.x
+			Local a:Float=Sqrt(totRadSq-dp*dp)
+			Local px:Float=P.x-vx2+a*ball.D.x
+			Local py:Float=P.y-vy2+a*ball.D.y
+			vx2=px-ball.P.x
+			vy2=py-ball.P.y
+			dp=vx2*ball.D.x+vy2*ball.D.y
+			If(Abs(dp)<=ball.L  And  dp >= 0.0)
+				vx2=px-P.x
+				vy2=py-P.y
+				Local len:Float=radius-ball.radius
+				If(len>0.0)
+					cdx=vx2/len
+					cdy=vy2/len
+				Else
+					cdx=ball.D.x
+					cdy=ball.D.y
+				Endif
+				px=P.x+cdx*radius
+				py=P.y+cdy*radius
+				vx2=px-pa.x
+				vy2=py-pa.y
+				Local dp2:Float=vx2*D.y-vy2*D.x
+				If(dp2>=0.0  And  ball.L>0.0)
+					cp.x=px
+					cp.y=py
+					tp=Self.cp
+					
+					dist=dp/ball.L
+					
+					Return dist
+				Endif
+				tp=Null
+				dist=INVALID_DISTANCE
+			Endif
+		Endif
+		
+		Local nextDist:Float = CollisionDistanceB2RW(ball,pa.x,pa.y,0.0)
+		
+		If(nextDist<dist)
+			dist=nextDist
+			np.x=tp.x
+			np.y=tp.y
+			tcdx=cdx
+			tcdy=cdy
+		Endif
+		
+		nextDist=CollisionDistanceB2RW(ball,pa.x+D.x*L,pa.y+D.y*L,0.0)
+		
+		If(nextDist<dist)
+			dist=nextDist
+			np.x=tp.x
+			np.y=tp.y
+			tcdx=cdx
+			tcdy=cdy
+		Endif
+		
+		tp=np
+		cdx=tcdx
+		cdy=tcdy
+		
+		If dist = INVALID_DISTANCE
+			tp=Null
+		Endif
+		
+		Return dist
+	End Method
+	
+	Method Bounce(ball:Ball,dx:Float,dy:Float) Override
+		
+		Self.Bounce2Fixed(ball,dx,dy)
+		
+	End Method
+	
+	Method Render(canvas:Canvas) Override
+		
+		If Self.animation
+			Self.animation.Render(canvas)
+		Endif
+		
+	End Method
+	
+	Method CollisionDistance2Ghost:Float(ball:GhostBall) Override
+		
+		Local dx:Float=.0
+		Local dy:Float=.0
+		Local dist:Float=Distance2Ghost(ball,P.x,P.y,radius)
+		
+		If(dist <> INVALID_DISTANCE)
+			Local px:Float=ball.position.x+ball.dx*dist
+			Local py:Float=ball.position.y+ball.dy*dist
+			Local vx:Float=px-P.x
+			Local vy:Float=py-P.y
+			Local ln:Float=Sqrt(vx*vx+vy*vy)
+			If(ln > 0.0)
+				dx=vx/ln
+				dy=vy/ln
+				px=P.x+dx*radius
+				py=P.y+dy*radius
+			Endif
+			vx=px-pa.x
+			vy=py-pa.y
+			If(vx*D.y-vy*D.x>=0.0)
+				
+				Return dist
+			Endif
+			dist=INVALID_DISTANCE
+		Endif
+		
+		Local vx2:Float=P.x-ball.position.x
+		Local vy2:Float=P.y-ball.position.y
+		Local vx1:Float=-vx2
+		Local vy1:Float=-vy2
+		Local totRadius:Float=radius-ball.radius
+		Local distSq:Float=vx2*vx2+vy2*vy2
+		Local totRadSq:Float=totRadius*totRadius
+		Local dp:Float=vx2*ball.dy-vy2*ball.dx
+		
+		if(Abs(dp)<radius)
+			vx2=dp*ball.dy
+			vy2=dp*-ball.dx
+			Local a:Float=Sqrt(totRadSq-dp*dp)
+			Local px2:Float=P.x-vx2+a*ball.dx
+			Local py2:Float=P.y-vy2+a*ball.dy
+			vx2=px2-ball.position.x
+			vy2=py2-ball.position.y
+			dp=vx2*ball.dx+vy2*ball.dy
+			
+			If(dp>=0.0)
+				vx2=px2-P.x
+				vy2=py2-P.y
+				
+				Local len:Float=radius-ball.radius
+				
+				If(len>0.0)
+					cdx=vx2/len
+					cdy=vy2/len
+				Else
+					cdx=ball.dx
+					cdy=ball.dy
+				Endif
+				
+				px2=P.x+cdx*radius
+				py2=P.y+cdy*radius
+				vx2=px2-pa.x
+				vy2=py2-pa.y
+				Local dp2:Float=vx2*D.y-vy2*D.x
+				
+				If(dp2>=0.0)
+					ball.cn.x=-cdx
+					ball.cn.y=-cdy
+					
+					Return dp
+				Endif
+				dist=INVALID_DISTANCE
+			Endif
+		Endif
+		
+		dx=ball.cn.x
+		dy=ball.cn.y
+		
+		Local nextDist:Float=Distance2Ghost(ball,pa.x,pa.y,0.0)
+		
+		If(nextDist<dist)
+			dist=nextDist
+			dx=ball.cn.x
+			dy=ball.cn.y
+		Endif
+		
+		nextDist=Distance2Ghost(ball,pa.x+D.x*L,pa.y+D.y*L,0.0)
+		
+		If(nextDist<dist)
+			dist=nextDist
+			dx=ball.cn.x
+			dy=ball.cn.y
+		Endif
+		
+		ball.cn.x=dx
+		ball.cn.y=dy
+		
+		Return dist
+		
+	End Method
+	
+End Class
+
+Class GhostBall
+	Field position:PVector2D
+	Field cn:PVector2D
+	Field radius:Float=.0
+	Field image:Image[]
+	Field dx:Float=.0
+	Field dy:Float=.0
+	Field frame:Int=0
+	Field delay:Int=0
+	Field stripImg:Image
+	Field time:Int=0
+	
+	Field colBall:Ball
+	Field distance:Float
+	
+	Method New(position:PVector2D,radius:Float,image:Image[],angle:Float,stpImg:Image)
+		Self.position=position
+		Self.cn=New PVector2D()
+		Self.radius=radius
+		Self.image=image
+		Self.dx=Cos(angle*ATR)
+		Self.dy=Sin(angle*ATR)
+		Self.frame=0
+		Self.delay=1
+		Self.stripImg=stpImg
+		Self.time=Millisecs()
+	End
+
+	Method Render(canvas:Canvas)
+		Local x:Float=position.x+dx*distance
+		Local y:Float=position.y+dy*distance
+		
+		If Millisecs()>time+delay
+			frame=(frame+1) Mod 3
+			time=Millisecs()
+		End
+		
+		Local ang:Float=-ATan2(dy,dx)
+		canvas.DrawImage(image[frame],x,y)
+		canvas.DrawImage(stripImg,position.x,position.y,ang,distance,1.0)
+		
+		If colBall
+			Local r2:Float=colBall.radius*2.0
+			canvas.DrawLine(colBall.P.x,colBall.P.y,colBall.P.x-cn.x*r2,colBall.P.y-cn.y*r2)
+			Local dp:Float=cn.x*-dx+cn.y*-dy
+			Local dx1:Float=dp*cn.x
+			Local dy1:Float=dp*cn.y
+			dp=cn.y*dx-cn.x*dy
+			dx1=dp*cn.y
+			dy1=-dp*cn.x
+			canvas.DrawLine(x,y,x+dx1*20.0,y+dy1*20.0)
+		Else
+			Local dp2:Float=cn.x*-dx+cn.y*-dy
+			Local dx12:Float=dp2*cn.x
+			Local dy12:Float=dp2*cn.y
+			dp2=cn.y*dx-cn.x*dy
+			dx12+=dp2*cn.y
+			dy12+=dp2*-cn.x
+			canvas.DrawLine(x,y,x+dx12*20.0,y+dy12*20.0)
+		End
+	End
+End

+ 558 - 0
bananas/billiards/PoolMod/BallAnimation.monkey2

@@ -0,0 +1,558 @@
+#Import "PoolMod"
+
+Class ObjectAnimation
+	Field P:PVector2D
+	Field color:Color
+
+	Method Reset:Void() Abstract
+	Method Render:Void(canvas:Canvas) Abstract
+End
+
+Class AImageLineStained Extends ObjectAnimation
+	Field angle:Float
+	Field Length:Float
+	Field image:Image
+	
+	Method New(P:PVector2D,vx:Float,vy:Float,image:Image,color:Color)
+		Self.P=P
+		Self.angle=-(ATan2(vy,vx))
+		Self.Length=Sqrt(vx*vx+vy*vy)
+		Self.image=image
+		Self.color=color
+	End
+
+	Method Reset:Void() Override
+	End
+
+	Method Render:Void(canvas:Canvas) Override
+		canvas.Color = color
+		canvas.DrawImage(image,P.x,P.y,angle,Length,1.0)
+	End
+End
+
+Class APixelLine Extends ObjectAnimation
+	Field vx:Float
+	Field vy:Float
+
+	Method New(P:PVector2D,vx:Float,vy:Float,color:Color)
+		self.P = P
+		self.vx= vx
+		self.vy= vy
+		self.color=color
+	End Method
+	
+	Method Reset:Void() Override
+	End Method
+
+	Method Rotate:Void(vx:Float,vy:float)
+	End Method
+	
+	Method Render:Void(canvas:Canvas) Override
+		canvas.Color = color
+		canvas.DrawLine(P.x,P.y,P.x+vx,P.y+vy)
+	End Method
+	
+End Class
+
+Class AImageArcStained Extends ObjectAnimation
+	Field image:Image
+
+	Method New(P:PVector2D,image:Image,color:Color)
+
+		self.P=P
+		self.image=image
+		self.color = color
+		
+	End Method
+	
+	Method Reset:Void() Override
+	End Method
+	
+	Method Rotate:Void(vx:Float,vy:float)
+	End Method
+	
+	Method Render:Void(canvas:Canvas) Override
+		canvas.Color = color
+		canvas.DrawImage(image,P.x,P.y) ',0)
+	End Method
+	
+End Class
+
+Class APixelArc Extends ObjectAnimation
+	Field radius:Float
+	Field startAngle:Float
+	Field endAngle:Float
+	Field stp:Float
+
+	Method New(P:PVector2D,radius:Float,startAngle:Float,endAngle:Float,color:Color)
+		Self.P=P
+		self.radius=radius
+		self.startAngle=startAngle
+		self.endAngle=endAngle
+		self.color = color
+		Self.stp=1.0/(RTA * radius)
+	End Method
+	
+	Method Reset:Void() Override
+	End Method
+	
+	Method Rotate:Void(vx:Float,vy:float)
+	End Method
+	
+	Method Render:Void(canvas:Canvas) Override
+	
+		canvas.Color = color
+		
+		If(startAngle = endAngle)
+			Return
+		Endif
+		
+		Local angle:Float=endAngle - startAngle
+		Local AccumAngle:Float=startAngle
+		Local rad2:Float=radius*2.0
+		
+		While(AccumAngle < startAngle+angle)
+			canvas.DrawRect(P.x+Cos(AccumAngle*ATR)*radius-0.5,P.y+Sin(AccumAngle*ATR) * radius-0.5,1.0,1.0)
+			AccumAngle += stp
+		Wend
+		
+	End Method
+	
+End Class
+
+Class Vec2D
+	Field x:Float
+	Field y:Float
+	Field len:Float
+	Field dx:Float
+	Field dy:Float
+
+	Method New(x1:Float, y1:Float, x2:Float, y2:Float)
+		x = x1
+		y = y1
+		
+		Local vx:Float = x2 - x1
+		Local vy:Float = y2 - y1
+		
+		If (vx<>0.0) Or (vy <> 0.0)
+			len = Sqrt(vx*vx + vy*vy)
+			dx = vx / len
+			dy = vy / len
+		Else
+			len = 0.0
+			dx  = 0.0
+			dy  = 0.0
+		Endif
+		
+	End Method
+	
+End Class
+
+
+Class Wall
+	Field name:String=""
+	Field image:Image
+	Field x1:Float
+	Field y1:Float
+	Field color:Color
+End
+
+Class Arc Extends Wall
+	Field radius:Float
+	Field startAngle:Float
+	Field endAngle:Float
+	Field cx:Float
+	Field cy:Float
+	
+	Method New(x1:Float,y1:Float,radius:Float,startAngle:Float,endAngle:Float,colx:Float,coly:Float,image:Image,color:Color)
+		Self.name="Arc"
+		Self.image=image
+		Self.radius=radius
+		Self.startAngle=startAngle
+		Self.endAngle=endAngle
+		Self.x1=x1
+		Self.y1=y1
+		Self.cx=colx
+		Self.cy=coly
+		Self.color=color
+	End
+
+End
+
+Class Line Extends Wall
+	Field x2:Float
+	Field y2:Float
+	
+	Method New(x1:Float,y1:Float,x2:Float,y2:Float,image:Image,color:Color)
+		Self.name="Line"
+		Self.image=image
+		Self.x1=x1
+		Self.y1=y1
+		Self.x2=x2
+		Self.y2=y2
+		Self.color=color
+	End
+
+End
+
+Class ABall3D Extends ObjectAnimation
+	Field oldP:PVector2D=New PVector2D()
+	Field textColor:Color
+	Field ball3d:Ball3d
+	Field image:Image
+	Method New(P:PVector2D,radius:Float,image:Image,ballColor:Color,textColor:Color,number:Int)
+		Self.P=P
+		Self.oldP.x=P.x
+		Self.oldP.y=P.y
+		Self.color= ballColor
+		Self.textColor= textColor
+		If number<16 ball3d = New Ball3d(number,radius-1.0)
+		Self.image=image
+	End
+
+	Method Rotate:Void(vx:Float,vy:Float)
+		If ball3d ball3d.Rotate(vx,vy)
+	End
+	
+	Method Reset:Void() Override
+		If ball3d ball3d.Reset()
+	End
+	
+	Method Render:Void(canvas:Canvas) Override
+		canvas.Color = New Color(0.0,0.0,0.0)
+		canvas.Alpha = .3
+		canvas.DrawImage(Self.image,P.x+3.0,P.y-1.0,0)
+		canvas.Alpha = 1.0
+		canvas.Color = color
+		canvas.DrawImage(image,P.x,P.y,0)
+		canvas.Color = textColor
+		If ball3d ball3d.Display(canvas,P.x,P.y)
+	End
+End
+
+Class Ball3d
+	Field distance:Float
+	Field radius:Float
+	Field nodeList:List<Node3d>
+	Global numbers:Int[][][]
+	
+	Method Decorate:Void(n:Int)
+		If(n=0)
+			Return
+		End
+		Local s:String=String(n)
+		Local len:Float=(s.Length)
+		For Local i:Float=0.0 Until len
+			Local t:Int=s[i]-48
+			For Local yaw:Float=0.0 Until 8.0
+				For Local pitch:Float=0.0 Until 5.0
+					If numbers[t][yaw][pitch]
+						Local node:Node3d= New Node3d()
+						node.z= -Cos((-20.0 + yaw*7.0)*ATR)*Cos((-20.0+pitch*7.0)*ATR)*(radius-1.0)
+						node.y=  Cos((-20.0 * len+i*32.0+yaw*7.0)*ATR)*Sin((-20.0*len+i*30.0+pitch*8.0)*ATR)*(radius-1.0)
+						node.x=  Sin((-20.0 + yaw*7.0)*ATR)*(radius-1.0)
+						node.sx=node.x
+						node.sy=node.y
+						node.sz=node.z
+						node.link=nodeList.AddLast(node)
+						Local node2:Node3d=New Node3d()
+						node2.x=node.x
+						node2.y=-node.y
+						node2.z=-node.z
+						node2.sx=node2.x
+						node2.sy=node2.y
+						node2.sz=node2.z
+						node2.link=nodeList.AddLast(node2)
+					End
+				End
+			End
+		End
+		If(n>8)
+			For Local i2:Float=0.0 Until 360.0
+				Local node3:Node3d= New Node3d()
+				node3.x=Cos(0.0)*Cos(i2*ATR)*radius
+				node3.y=Cos(0.0)*Sin(i2*ATR)*radius
+				node3.z=Sin(0.0)*radius
+				node3.sx=node3.x
+				node3.sy=node3.y
+				node3.sz=node3.z
+				node3.link=nodeList.AddLast(node3)
+			End
+		End
+	End
+	
+	Method New(n:Int,rad:Float)
+		distance=500.0
+		radius=rad
+		nodeList=New List<Node3d>()
+		If Not numbers.Length
+			numbers=New Int[][][](New Int[][](New Int[](0,0,1,1,0),New Int[](0,1,0,0,1),New Int[](0,1,0,0,1),New Int[](0,1,0,0,1),New Int[](0,1,0,0,1),New Int[](0,1,0,0,1),New Int[](0,0,1,1,0),New Int[](0,0,0,0,0)),
+							 	  New Int[][](New Int[](0,0,1,0,0),New Int[](0,1,1,0,0),New Int[](0,0,1,0,0),New Int[](0,0,1,0,0),New Int[](0,0,1,0,0),New Int[](0,0,1,0,0),New Int[](0,1,1,1,0),New Int[](0,0,0,0,0)),
+							  	  New Int[][](New Int[](0,1,1,1,0),New Int[](0,1,0,0,1),New Int[](0,0,0,0,1),New Int[](0,0,0,1,0),New Int[](0,0,1,0,0),New Int[](0,1,0,0,0),New Int[](0,1,1,1,1),New Int[](0,0,0,0,0)),
+							  	  New Int[][](New Int[](0,1,1,1,0),New Int[](0,0,0,0,1),New Int[](0,0,0,0,1),New Int[](0,0,1,1,0),New Int[](0,0,0,0,1),New Int[](0,0,0,0,1),New Int[](0,1,1,1,0),New Int[](0,0,0,0,0)),
+							  	  New Int[][](New Int[](0,0,0,1,0),New Int[](0,0,1,1,0),New Int[](0,1,0,1,0),New Int[](1,0,0,1,0),New Int[](1,1,1,1,1),New Int[](0,0,0,1,0),New Int[](0,0,0,1,0),New Int[](0,0,0,0,0)),
+							  	  New Int[][](New Int[](0,1,1,1,1),New Int[](0,1,0,0,0),New Int[](0,1,0,0,0),New Int[](0,0,1,1,0),New Int[](0,0,0,0,1),New Int[](0,0,0,0,1),New Int[](0,1,1,1,0),New Int[](0,0,0,0,0)),
+							  	  New Int[][](New Int[](0,0,1,1,0),New Int[](0,1,0,0,0),New Int[](0,1,0,0,0),New Int[](0,1,0,1,0),New Int[](0,1,0,0,1),New Int[](0,1,0,0,1),New Int[](0,0,1,1,0),New Int[](0,0,0,0,0)),
+							  	  New Int[][](New Int[](0,1,1,1,1),New Int[](0,0,0,0,1),New Int[](0,0,0,1,0),New Int[](0,0,1,0,0),New Int[](0,0,1,0,0),New Int[](0,0,1,0,0),New Int[](0,0,1,0,0),New Int[](0,0,0,0,0)),
+							  	  New Int[][](New Int[](0,0,1,1,0),New Int[](0,1,0,0,1),New Int[](0,1,0,0,1),New Int[](0,0,1,1,0),New Int[](0,1,0,0,1),New Int[](0,1,0,0,1),New Int[](0,0,1,1,0),New Int[](0,0,0,0,0)),
+							  	  New Int[][](New Int[](0,0,1,1,0),New Int[](0,1,0,0,1),New Int[](0,1,0,0,1),New Int[](0,0,1,1,1),New Int[](0,0,0,0,1),New Int[](0,1,0,0,1),New Int[](0,0,1,1,0),New Int[](0,0,0,0,0)))
+		End
+		Decorate(n)
+	End
+
+	Method Rotate_z(a:Float)
+		If nodeList.Empty Return
+		Local cx:Float=Cos(a*ATR)
+		Local cy:Float=Sin(a*ATR)
+		Local link:List<Node3d>.Node=nodeList.First.link
+		While link.Value
+			Local node:Node3d=link.Value
+			Local tx:Float=node.x*cx-node.y*cy
+			Local ty:Float=node.x*cy+node.y*cx
+			node.y=ty
+			node.x=tx
+			link=link.Succ
+		End
+	End
+	
+	Method Rotate_y(a:Float)
+		If nodeList.Empty Return
+		Local cx:Float=Cos(a*ATR)
+		Local cy:Float=Sin(a*ATR)
+		Local link:List<Node3d>.Node = nodeList.FirstNode()
+		While link.Value
+			Local node:Node3d=link.Value
+			Local tz:Float=node.z*cx-node.x*cy
+			Local tx:Float=node.z*cy+node.x*cx
+			node.z=tz
+			node.x=tx
+			link=link.Succ
+		End
+	End
+	
+	Method Rotate(vx:Float,vy:Float)
+		Local roll:Float=Sqrt(vx*vx+vy*vy)
+		Local rot:Float= ATan2(vy,vx) * RTA
+		Self.Rotate_z(-rot)
+		Self.Rotate_y(roll*radius)
+		Self.Rotate_z(rot)
+	End
+	
+	Method Reset()
+		If nodeList.Empty Return
+		Local link:List<Node3d>.Node=nodeList.FirstNode()
+		While link.Value
+			Local node:Node3d=link.Value
+			node.x=node.sx
+			node.y=node.sy
+			node.z=node.sz
+			link=link.Succ
+		End
+	End
+	
+	Method Display:Void(canvas:Canvas,x:Float,y:Float)
+		If nodeList.Empty Return
+		Local link:List<Node3d>.Node=nodeList.FirstNode()
+		While link.Value
+			Local node:Node3d=link.Value
+			If(node.z>-1.0) canvas.DrawOval(x+node.x,y+node.y,1.0,1.0)
+			link=link.Succ
+		End
+	End
+End
+
+Class Node3d
+	Field z:Float
+	Field y:Float
+	Field x:Float
+	Field sx:Float
+	Field sy:Float
+	Field sz:Float
+	Field link:List<Node3d>.Node
+End
+
+Class RailAnimation
+	Field jobList:List<Ball>
+	Field settledList:List<RailBall>
+	Field movingList:List<RailBall>
+	Field stopPVector2Ds:Vec2D[]
+	Field finalStop:Int=0
+	Field showStops:Int=0
+	Field index:Int=0
+
+	Method New()
+		jobList= New List<Ball>
+		settledList= New List<RailBall>
+		movingList= New List<RailBall>
+		stopPVector2Ds=New Vec2D[](New Vec2D(375.0,340.0,366.0,370.0),New Vec2D(366.0,370.0,355.0,380.0),New Vec2D(355.0,380.0,325.0,380.0),
+						 		   New Vec2D(325.0,380.0,305.0,380.0),New Vec2D(305.0,380.0,285.0,380.0),New Vec2D(285.0,380.0,265.0,380.0),
+						 		   New Vec2D(265.0,380.0,245.0,380.0),New Vec2D(245.0,380.0,225.0,380.0),New Vec2D(225.0,380.0,205.0,380.0),
+						 		   New Vec2D(205.0,380.0,185.0,380.0),New Vec2D(185.0,380.0,165.0,380.0),New Vec2D(165.0,380.0,145.0,380.0),
+						 		   New Vec2D(145.0,380.0,125.0,380.0),New Vec2D(125.0,380.0,105.0,380.0),New Vec2D(105.0,380.0, 85.0,380.0),
+						 		   New Vec2D( 85.0,380.0, 65.0,380.0),New Vec2D( 65.0,380.0, 45.0,380.0),New Vec2D( 45.0,380.0, 25.0,380.0),
+						 		   New Vec2D( 25.0,380.0, 25.0,380.0))
+		finalStop=18
+	End
+	
+	Method ShowStops:Void(show:Int)
+		showStops=show
+	End
+	
+	Method Reset:Void()
+		jobList.Clear()
+		movingList.Clear()
+		settledList.Clear()
+		index=0
+		finalStop=18
+	End
+	
+	Method AddJob:Void(b:Ball)
+		jobList.AddFirst(b)
+	End
+	
+	Method RemoveCueBall:Void()
+		If Not jobList.Empty
+			For Local b:Ball = Eachin jobList
+				If(b.num=16)
+					jobList.RemoveEach(b)
+					Return
+				End
+			End
+		End
+		If Not movingList.Empty
+			For Local b2:RailBall = Eachin movingList
+				If(b2.ball.num=16)
+					movingList.RemoveEach(b2)
+					Return
+				End
+			End
+		End
+		If Not settledList.Empty
+			For Local b3:RailBall = Eachin settledList
+				If(b3.ball.num=16)
+					settledList.RemoveEach(b3)
+					finalStop=b3.index
+				End
+			End
+		End
+		If Not settledList.Empty
+			Local c:Int=0
+			For Local b4:RailBall = Eachin settledList.Backwards()
+				If(b4.index<finalStop)
+					settledList.RemoveEach(b4)
+					movingList.AddLast(b4)
+				End
+			End
+		End
+	End
+	
+	Method Update:Void(spd:Float)
+		If movingList.Empty And jobList.Empty
+			Return
+		End
+		If movingList.Empty
+			Local b:Ball=jobList.RemoveLast()
+			If(b<>Null)
+				Local rb:RailBall=New RailBall(b,stopPVector2Ds[0].x,stopPVector2Ds[0].y)
+				movingList.AddLast(rb)
+			End
+		Else
+			If  Not jobList.Empty
+				Local rb2:RailBall=movingList.Last
+				If((rb2)<>Null)
+					Local vx1:Float=rb2.ball.P.x-stopPVector2Ds[0].x
+					Local vy1:Float=rb2.ball.P.y-stopPVector2Ds[0].y
+					Local dp:Float=vx1*stopPVector2Ds[0].dx+vy1*stopPVector2Ds[0].dy
+					If(Abs(dp)>rb2.ball.radius+20.0)
+						Local b2:Ball=jobList.RemoveLast()
+						movingList.AddLast(New RailBall(b2,stopPVector2Ds[0].x,stopPVector2Ds[0].y))
+					End
+				End
+			End
+		End
+		Local vx:Float=.0
+		Local vy:Float=.0
+		For Local rb3:RailBall = Eachin movingList
+			vx=stopPVector2Ds[rb3.index].dx*spd
+			vy=stopPVector2Ds[rb3.index].dy*spd
+			rb3.ball.P.x+=vx
+			rb3.ball.P.y+=vy
+		End
+		For Local rb4:RailBall = Eachin movingList
+			If(rb4.index<finalStop)
+				Local vx12:Float=rb4.ball.P.x-stopPVector2Ds[rb4.index].x
+				Local vy12:Float=rb4.ball.P.y-stopPVector2Ds[rb4.index].y
+				Local dp1:Float=vx12*stopPVector2Ds[rb4.index].dx+vy12*stopPVector2Ds[rb4.index].dy
+				If(Abs(dp1)>=stopPVector2Ds[rb4.index].len)
+					Local len:Float=Abs(dp1)-stopPVector2Ds[rb4.index].len
+					vx12=stopPVector2Ds[rb4.index].dx*stopPVector2Ds[rb4.index].len
+					vy12=stopPVector2Ds[rb4.index].dy*stopPVector2Ds[rb4.index].len
+					rb4.ball.P.x=stopPVector2Ds[rb4.index].x+vx12
+					rb4.ball.P.y=stopPVector2Ds[rb4.index].y+vy12
+					rb4.animation.Rotate(vx*1.2,vy*1.2)
+					rb4.index+=1
+					If(rb4.index=finalStop)
+						movingList.RemoveEach(rb4)
+						settledList.AddLast(rb4)
+						rb4.animation.Rotate(vx*1.2,vy*1.2)
+						If(finalStop<18)
+							media.PlayBallCol(2)
+						End
+						finalStop-=1
+					Else
+						vx12=stopPVector2Ds[rb4.index].dx*len
+						vy12=stopPVector2Ds[rb4.index].dy*len
+						rb4.ball.P.x+=stopPVector2Ds[rb4.index].dx*len
+						rb4.ball.P.y+=stopPVector2Ds[rb4.index].dy*len
+					End
+				Else
+					rb4.animation.Rotate(vx,vy)
+				End
+			Else
+				If(rb4.index=finalStop)
+					movingList.RemoveEach(rb4)
+					rb4.index=finalStop
+					rb4.ball.P.x=stopPVector2Ds[finalStop].x
+					rb4.ball.P.y=stopPVector2Ds[finalStop].y
+					settledList.AddLast(rb4)
+				End
+			End
+		End
+	End
+	
+	Method Render:Void(canvas:Canvas)
+		If showStops=1
+			canvas.Color = New Color(.95,.95,.4) '(240.0,240.0,100.0)
+			Local i:Int=0
+			While(i < stopPVector2Ds.Length)
+				Local stop:Vec2D=stopPVector2Ds[i]
+				canvas.DrawCircle(stop.x,stop.y,3.0)
+				canvas.DrawLine(stop.x,stop.y,stop.x+stop.dx*stop.len,stop.y+stop.dy*stop.len)
+				i=i+1
+			End
+		End
+		For Local railBall:RailBall = Eachin settledList
+			railBall.Render(canvas)
+		End
+		For Local moving:RailBall = Eachin movingList
+			moving.Render(canvas)
+		End
+	End
+End
+
+Class RailBall
+	Field ball:Ball
+	Field index:Int=0
+	Field animation:ABall3D
+	Method New(b:Ball,x:Float,y:Float)
+		b.P.x=x
+		b.P.y=y
+		ball=b
+		animation=Cast<ABall3D>(b.animation)
+		index=0
+	End
+
+	Method Render:Void(canvas:Canvas)
+		ball.Render(canvas)
+	End
+End

+ 398 - 0
bananas/billiards/PoolMod/Common.monkey2

@@ -0,0 +1,398 @@
+#Import "PoolMod"
+
+Const RTA:Float = 180.0/Pi
+Const ATR:Float = Pi/180.0
+Const INVALID_DISTANCE:Float = 100000000
+Const DEVICE_WIDTH:Int = 640
+Const DEVICE_HEIGHT:Int = 480
+
+'***********************************************************************************
+'
+'		renders a circle at a distance relative to a ball current position
+'
+'***********************************************************************************
+
+Function RenderAtDistance:Void(canvas:Canvas,ball:Ball,distance:Float)
+	If Not ball.image
+		RenderCircle(canvas,ball.P.x+ball.V.x*distance,ball.P.y+ball.V.y*distance,ball.radius)
+	Else
+		canvas.DrawImage(ball.image,ball.P.x+ball.V.x*distance,ball.P.y+ball.V.y*distance)
+	Endif
+End Function
+
+''***********************************************************************************
+'
+'		renders an arc at position x,y and radius,
+'		starting angle, and ending angle.
+'
+'************************************************************************************
+
+Function RenderArc:Void(canvas:Canvas,x:Float, y:Float, radius:Float, startAngle:Float, endAngle:Float)
+	
+	'If angles are equal nothing to draw
+	If startAngle = endAngle Then Return
+	
+	'make the arc clockwise
+	If startAngle > endAngle
+		' swap angles if order is counter-clockwise
+		Local ta:Float = endAngle
+		endAngle = startAngle
+		startAngle = ta
+	Endif
+	
+	'number of degrees to render
+	Local angle:Float = endAngle - startAngle
+	
+	' no more than 360 degrees to render
+	If angle > 360.0 angle = 360.0
+	
+	' set the step to advance one unit at a tme
+	Local stp:Float = 1.0/(RTA * radius)
+	
+	'set the accumulator to the starting angle
+	Local AccumAngle:Float = startAngle
+	
+	' repeat until the arc is completely drawn
+	While AccumAngle < (startAngle+angle)
+		
+		'draw a pixel around the arc
+		canvas.DrawPoint(x + Cos(AccumAngle*ATR) * radius, y + Sin(AccumAngle*ATR) * radius)
+		
+		'advance to next angle
+		AccumAngle += stp
+	
+	Wend
+End Function 
+
+'***********************************************************************************
+'
+'		draws a circle at x,y and radius
+'
+'***********************************************************************************
+
+Function RenderCircle:Void(canvas:Canvas,x:Float,y:Float,radius:Float)
+		'start angle
+		Local angle:Float = 0.0
+		
+		' distance between each point
+		Local stp:Float = 1.0/(RTA * radius)
+	
+		'rotate upto 360 degrees
+		While angle < 360.0
+	
+			canvas.DrawPoint(x + Cos(angle*ATR) * radius, y + Sin(angle*ATR) * radius)
+			
+			'increment the position to draw the next point
+			angle += stp
+	
+		Wend
+
+End Function
+
+'***********************************************************************************
+'
+'	Draws a line with of specific width
+'
+'
+'***********************************************************************************
+
+Function DrawLine_W:Void(canvas:Canvas,XPos:Float,YPos:Float,XPos2:Float,YPos2:Float,Thickness:Float=3,roundedEnds:Int=False)
+	
+	Local Coords:Float[] = New Float[8] 
+	
+	Local vx:Float = XPos2 - XPos
+	Local vy:Float = YPos2 - YPos
+	
+	Local Angle:Float = ATan2(vy,vx)
+	
+	Local c:Float = Cos(Angle*ATR)
+	Local s:Float = Sin(Angle*ATR)
+	Local LineLength:Float=vx*c+vy*s
+	
+	Local cl:Float = LineLength*c
+	Local sl:Float = LineLength*s
+	
+	Local r:Float = Thickness/2.0
+	
+	Local sr:Float = s*r
+	Local cr:Float = c*r
+	
+	'Left top coords
+	Coords[0]=XPos+sr
+	Coords[1]=YPos-cr
+	
+	'Right top coords
+	Coords[2]=cl+XPos+sr
+	Coords[3]=sl+YPos-cr
+	
+	'Right bottom coords
+	Coords[4]=cl-sr+XPos
+	Coords[5]=sl+cr+YPos
+	
+	'Left bottom coords
+	Coords[6]=XPos-sr
+	Coords[7]=YPos+cr
+	
+	canvas.DrawPoly(Coords)	
+	
+	If roundedEnds = True	
+		canvas.DrawCircle(XPos,YPos,r)
+		canvas.DrawCircle(XPos2,YPos2,r)
+	Endif
+
+End Function
+
+
+Function LoadFrames:Image[](img:Image,x:Int,y:Int,width:Int,height:Int,count:Int)
+	Local frames:Image[] = New Image[count]
+	Local index:Int = 0
+	Local posx:Int = x
+	Local posy:Int = y
+	While index < count
+		If posx > (img.Width-width)
+			Print posx
+			posx = 0
+			posy += height	
+		Endif
+		frames[index] = New Image(img,posx,posy,width,height)
+		index += 1
+		posx += width
+	Wend
+	Return frames
+End Function
+
+
+Class sysFont
+	Global font:Image[]
+	Global width:Int
+	Global height:Int
+	Global firstChar:Int
+	
+	Function Set(image:Image[],first:Int)
+		font = image
+		width = image[0].Width
+		height = image[0].Height
+	End Function
+	
+	Function Draw(canvas:Canvas,text:String,x:Float,y:Float)
+		For Local i:Int=0 Until text.Length
+			Local ch:Int=text[i]-firstChar
+			If ch>=0 And ch<font.Length
+				canvas.DrawImage(font[ch],x+i*width,y)
+			Endif
+		Next		
+	End Function
+	
+End Class
+
+Function SetFont( font:Image[],firstChar:Int=32 )
+	sysFont.Set(font,firstChar)
+End
+
+Function GetFont:Image[]()
+	Return sysFont.font
+End
+
+Function TextWidth:Float( text:string )
+	If sysFont.font 
+		Return text.Length * sysFont.width
+	Endif
+	RuntimeError("No Font Available")
+	Return 0
+End
+
+Function TextHeight:Float()
+	If sysFont.font 
+		Return sysFont.height
+	Endif
+	RuntimeError("No Font Available")
+	Return 0
+End
+
+Function FontHeight:Float()
+	If sysFont.font 
+		Return sysFont.height
+	Endif
+	RuntimeError("No Font Available")
+	Return 0
+End
+
+Function DrawText(canvas:Canvas,text:String,x:Float,y:Float )
+	sysFont.Draw(canvas,text,x,y)
+End
+
+
+Class PVector2D
+	Field x:Float
+	Field y:Float
+	
+	Method New()
+	End Method
+	
+	Method New(x:Float,y:Float)
+		Set(x,y)
+	End Method
+	
+	Method New(x1:Float,y1:Float,x2:Float,y2:Float)
+		Set(x2-x1,y2-y1)
+	End Method
+	
+	Method New(v:PVector2D)
+		Set(v)
+	End Method
+	
+	Method Set:PVector2D(v:PVector2D)
+		x = v.x
+		y = v.y
+		Return Self
+	End Method
+
+	Method Set:PVector2D(x:Float,y:Float)
+		Self.x = x
+		Self.y = y
+		Return Self
+	End Method
+		
+	Method Add:PVector2D(x:Float,y:Float)
+		Self.x += x
+		Self.y += y
+		Return Self
+	End Method
+	
+	Method Add:PVector2D(v:PVector2D)
+		Self.x += v.x
+		Self.y += v.y
+		Return Self
+	End Method
+	
+	Method Add:PVector2D(value:Float)
+		Self.x += value
+		Self.y += value
+		Return Self
+	End Method
+	
+	Method Subtract:PVector2D(x:Float,y:Float)
+		Self.x -= x
+		Self.y -= y
+		Return Self
+	End Method
+	
+	Method Subtract:PVector2D(v:PVector2D)
+		Self.x -= v.x
+		Self.y -= v.y
+		Return Self
+	End Method
+	
+	Method Subtract:PVector2D(value:Float)
+		Self.x -= value
+		Self.y -= value
+		Return Self
+	End Method
+	
+	Method Multiply:PVector2D(x:Float,y:Float)
+		Self.x *= x
+		Self.y *= y
+		Return Self
+	End Method
+	
+	Method Multiply:PVector2D(v:PVector2D)
+		Self.x *= v.x
+		Self.y *= v.y
+		Return Self
+	End Method
+	
+	Method Multiply:PVector2D(value:Float)
+		Self.x *= value
+		Self.y *= value
+		Return Self
+	End Method
+	
+	Method Divide:PVector2D(x:Float,y:Float)
+		Self.x /= x
+		Self.y /= y
+		Return Self
+	End Method
+	
+	Method Divide:PVector2D(v:PVector2D)
+		Self.x /= v.x
+		Self.y /= v.y
+		Return Self
+	End Method
+	
+	Method Divide:PVector2D(value:Float)
+		Self.x /= value
+		Self.y /= value
+		Return Self
+	End Method
+	
+	Method DotProduct:Float(x:Float,y:Float)
+		Return Self.x * x + Self.y * y
+	End Method
+	
+	Method DotProduct:Float(v:PVector2D)
+		Return Self.x * v.x + Self.y * v.y
+	End Method
+	
+	Method PerpDotProduct:Float(x:Float,y:Float)
+		Return Self.x * y - Self.y * x
+	End Method
+	
+	Method PerpDotProduct:Float(v:PVector2D)
+		Return Self.x * v.y - Self.y * v.x
+	End Method
+	
+	Method MagnitudeSquare:Float()
+		Return Self.x * Self.x + Self.y * Self.y
+	End Method
+	
+	Method Magnitude:Float()
+		Return Sqrt(MagnitudeSquare())
+	End Method
+	
+	Method LeftNormal()
+		Local n:Float = y
+		y = -x 
+		x = n
+	End Method
+	
+	Method RightNormal()
+		Local n:Float = y
+		y = x
+		x = -n
+	End Method
+	
+	Method Normalize:PVector2D()
+		Divide(Magnitude())
+		Return Self
+	End Method
+		
+	Method GetAngle:Float()
+		Return ATan2(y,x)
+	End Method
+	
+	Method ToString:String()
+		Return x+"  "+y
+	End Method
+	
+	Method Draw:Void(canvas:Canvas,x:Float,y:Float)
+		canvas.DrawLine(x,y,x+x,y+y)
+	End Method
+	
+End Class
+
+Function Projection:PVector2D(this:PVector2D,into:PVector2D,destination:PVector2D=Null)
+	
+	If destination = Null destination = New PVector2D
+		
+	Return destination.Set(into).Normalize().Multiply(destination.DotProduct(this))
+	
+End Function
+
+Function CrossProjection:PVector2D(this:PVector2D,into:PVector2D,destination:PVector2D=Null)
+	
+	If destination = Null destination = New PVector2D
+	
+	Return destination.Set(into.y,-into.x).Normalize().Multiply(destination.DotProduct(this))
+		
+End Function
+

+ 272 - 0
bananas/billiards/PoolMod/Engine.monkey2

@@ -0,0 +1,272 @@
+#Import "PoolMod"
+'******************************************************************************************
+'
+'		elastic/non elastic ball/cirlcle collision engine
+'
+'******************************************************************************************
+
+' Collision storage.
+Class ElasticEngine
+	Field wallList:List<VectorObject>
+	Field ballList:List<Ball>
+	Field colls:CollisionTrend[] = New CollisionTrend[99]
+	
+	Field totalCol:Int
+	Field nearesBall:Ball
+	Field colCount:Int
+	Field firstCollision:Ball
+	
+	Method New()
+		wallList= New List<VectorObject>
+		ballList= New List<Ball>
+		For Local i:Int=0 Until 99
+			colls[i]= New CollisionTrend()
+		End
+	End
+	
+	Method SetGravity(vx:Float,vy:Float)
+		VectorObject.SetGlobalGravity(vx,vy)
+	End
+	
+	Method SetFriction(f:Float)
+		VectorObject.SetGlobalFriction(f)
+	End
+	
+	Method RemoveWalls()
+		wallList.Clear()
+	End
+	
+	Method AddWall:List<VectorObject>.Node(t_wall:VectorObject)
+		Return wallList.AddLast(t_wall)
+	End
+	
+	Method AddLineWallImage:LineWall(x1:Float,y1:Float,x2:Float,y2:Float,image:Image,color:Color)
+		Local line:LineWall= New LineWall(x1,y1,x2,y2,Null,1.0,1.0)
+		Local animation:AImageLineStained= New AImageLineStained(line.P,line.V.x,line.V.y,image,color)
+		line.SetAnimation(animation)
+		AddWall(line)
+		Return line
+	End
+	
+	Method AddLineWallOutline:LineWall(x1:Float,y1:Float,x2:Float,y2:Float,image:Image,color:Color)
+		Local line:LineWall=New LineWall(x1,y1,x2,y2,Null,1.0,1.0)
+		Local animation:APixelLine= New APixelLine(line.P,line.V.x,line.V.y,color)
+		line.SetAnimation(animation)
+		AddWall(line)
+		Return line
+	End
+	
+	Method AddArcWallImage:ArcWall(x:Float,y:Float,radius:Float,startAngle:Float,endAngle:Float,image:Image,color:Color)
+		Local arc:ArcWall=New ArcWall(x,y,radius,startAngle,endAngle,Null,1.0,1.0)
+		Local animation:AImageArcStained= New AImageArcStained(arc.P,image,color)
+		arc.SetAnimation(animation)
+		AddWall(arc)
+		Return arc
+	End
+	
+	Method AddArcWallOutline:ArcWall(x:Float,y:Float,radius:Float,startAngle:Float,endAngle:Float,color:Color)
+		Local arc:ArcWall=New ArcWall(x,y,radius,startAngle,endAngle,Null,1.0,1.0)
+		Local animation:APixelArc=New APixelArc(arc.P,radius,startAngle,endAngle,color)
+		arc.SetAnimation(animation)
+		AddWall(arc)
+		Return arc
+	End
+	
+	Method AddBall:List<Ball>.Node(ball:Ball) 
+		ball.node=ballList.AddLast(ball)
+		Return ball.node
+	End
+	
+	Method Add3DBall:Ball(x:Float,y:Float,radius:Float,vx:Float,vy:Float,image:Image,color:Color,txtColor:Color,number:Int,displayFloat:Int)
+		Local ball:Ball=New Ball(x,y,radius,vx,vy,Null,1.0,1.0,1.0,number,False)
+		Local animation:ObjectAnimation= New ABall3D(ball.P,radius,image,color,txtColor,number)
+		ball.SetAnimation(animation)
+		AddBall(ball)
+		Return ball
+	End
+	
+	Method Process()
+		
+		Local distance:Float=INVALID_DISTANCE
+		Local done:Int=0
+		nearesBall = Null
+		Local nearestObject:VectorObject
+		colCount = -1
+		For Local ball:Ball = Eachin ballList
+			For Local ball2:Ball = Eachin ballList.Backwards()
+				If ball=ball2 Exit
+				If(Not ball.IsMoving() And Not ball2.IsMoving()) Continue
+				Local balla:Ball
+				Local ballb:Ball
+				If(ball.L>=ball2.L)
+					balla=ball2
+					ballb=ball
+				Else
+					balla=ball
+					ballb=ball2
+				End
+				Local oldL:Float=ballb.L
+				Local d:Float=balla.Distance(ballb)
+				If d=INVALID_DISTANCE
+					Continue
+				Else
+					If d<distance
+						distance=d
+						colCount=0
+						colls[colCount].ball=ballb
+						colls[colCount].obj= balla
+						colls[colCount].cdx=balla.cdx
+						colls[colCount].cdy=balla.cdy
+						colls[colCount].oldL=oldL
+					Else
+						If d=distance
+							colCount+=1
+							colls[colCount].ball=ballb
+							colls[colCount].obj= balla
+							colls[colCount].cdx=balla.cdx
+							colls[colCount].cdy=balla.cdy
+							colls[colCount].oldL=oldL
+						End
+					End
+				End
+			End
+			For Local t_wall:VectorObject = Eachin wallList
+				Local d2:Float=t_wall.Distance(ball)
+				If d2 = INVALID_DISTANCE
+					Continue
+				Else
+					If d2 < distance
+						distance=d2
+						colCount=0
+						colls[colCount].ball=ball
+						colls[colCount].obj=t_wall
+						colls[colCount].cdx=t_wall.cdx
+						colls[colCount].cdy=t_wall.cdy
+						colls[colCount].oldL=0
+					Else
+						If d2 = distance
+							colCount+=1
+							colls[colCount].ball=ball
+							colls[colCount].obj=t_wall
+							colls[colCount].cdx=t_wall.cdx
+							colls[colCount].cdy=t_wall.cdy
+							colls[colCount].oldL=0
+						End
+					End
+				End
+			End
+		End
+		If distance = INVALID_DISTANCE
+			distance=1.0
+			done=True
+		End
+		
+		For Local ball3:Ball = Eachin ballList
+			ball3.Advance(distance)
+		End
+		
+		For Local i:Int=0 to colCount
+			colls[i].obj.Bounce(colls[i].ball,colls[i].cdx,colls[i].cdy)
+			If(Not((firstCollision)<>Null))
+				Local b1:Ball=Cast<Ball>(colls[i].obj)
+				Local b2:Ball=colls[i].ball
+				If(((b1)<>Null) And b1.num=16)
+					firstCollision=b2
+				Else
+					If b2.num = 16 And b1 firstCollision=b1
+				End
+			End
+		End
+		If totalCol>=0 totalCol+=colCount+1
+		If Not done Process()
+	End
+	
+	Method Update(timeFrame:Float)
+	
+		If(timeFrame = 0.0) Return
+		If(timeFrame > 3.0) timeFrame=3.0
+		
+		For Local ball:Ball = Eachin ballList
+			ball.updateIn(timeFrame)
+		End
+		totalCol = 0
+		Process()
+		For Local ball2:Ball = Eachin ballList
+			ball2.updateOut(timeFrame)
+		End	
+	End
+	
+	Method RemoveBall(ball:Ball)
+		ball.node.Remove()
+	End
+	
+	Method BallsMoving:Bool()
+		For Local b:Ball = Eachin ballList
+			If b.IsMoving() Return True
+		End
+		Return False
+	End
+	
+	Method GetFirstCollision:Ball()
+		Return firstCollision
+	End
+	
+	Method ClearFirstCollision()
+		firstCollision = Null
+	End
+	
+	Method Render(canvas:Canvas)
+		For Local ball:Ball = Eachin ballList
+			If(ball.P.x<0.0 Or ball.P.x>DEVICE_WIDTH Or ball.P.y<0.0 Or ball.P.y>DEVICE_HEIGHT)
+				RuntimeError("ball exited screen ")
+			End
+			ball.Render(canvas)
+		End
+		'For Local wall := Eachin wallList
+		'	wall.Render(canvas)
+		'Next
+		Return
+	End
+	
+	Method CollisionDistance2Ghost(ghost:GhostBall)
+		Local distance:Float=INVALID_DISTANCE
+		Local cdx:Float, cdy:Float
+		For Local b:Ball = Eachin ballList
+			cdx=ghost.cn.x
+			cdy=ghost.cn.y
+			Local dist:Float=b.CollisionDistance2Ghost(ghost)
+			If(dist<distance)
+				distance=dist
+				ghost.colBall=b
+			Else
+				ghost.cn.x=cdx
+				ghost.cn.y=cdy
+			End
+		End
+		For Local w:VectorObject = Eachin wallList
+			cdx=ghost.cn.x
+			cdy=ghost.cn.y
+			Local dist2:Float=w.CollisionDistance2Ghost(ghost)
+			If(dist2<distance)
+				ghost.colBall = Null
+				distance=dist2
+			Else
+				ghost.cn.x=cdx
+				ghost.cn.y=cdy
+			End
+		End
+		ghost.distance=distance
+	End
+End
+
+Class CollisionTrend
+	Field temp:PVector2D
+	Method New()
+		temp=New PVector2D()
+	End
+	Field ball:Ball
+	Field obj:VectorObject
+	Field cdx:Float
+	Field cdy:Float
+	Field oldL:Float
+End

+ 229 - 0
bananas/billiards/PoolMod/FileSystem.monkey2

@@ -0,0 +1,229 @@
+#Import "<mojo>"
+
+Class DataConversion
+
+	Method StringToInt:Int(val:String)
+		Return Int(val)
+	End
+
+	Method IntToString:String(val:Int)
+		Return String(val)
+	End
+End
+
+Class FileSystem Extends DataConversion
+	
+	Field fileData:String=""
+	
+	Field index:StringMap<FileStream>
+	
+	Field _header:String="MKYDATA"
+	
+	Method LoadAll:Void()
+	
+		Local numFiles:Int=0
+		
+		Local stream:FileStream
+		
+		Local len:Int=0
+		
+		Local ptri:Int=0
+		
+'		Self.fileData=LoadState() '********************************************************* fix this ****************
+		
+		Self.index=New StringMap<FileStream>
+		
+		If(Self.fileData.Length>0)
+			
+			If(Self.fileData.StartsWith(Self._header))
+				
+				Self.index.Clear()
+				
+				ptri+=Self._header.Length
+				
+				numFiles=Self.StringToInt(Self.fileData.Mid(ptri,3)) '.[ptr..ptr+3])
+				
+				ptri+=3
+				
+				If(numFiles>0)
+					
+					For Local n:Int=1 To numFiles
+						
+						stream=New FileStream()
+						
+						len=Int(Self.fileData.Mid(ptri,3)) ',[ptr..ptr+3])
+						
+						ptri+=3
+						
+						If(len>0)
+						
+							stream.filename=Self.fileData.Mid(ptri,len) '[ptr..ptr+len]
+						
+							ptri+=len
+						
+						End
+						
+						len=Int(Self.fileData.Mid(ptri,3)) '[ptr..ptr+3])
+						
+						ptri+=3
+						
+						If(len>0)
+						
+							stream.data=Self.fileData.Mid(ptri,len) '[ptr..ptr+len]
+						
+							ptri+=len
+						
+						End
+						
+						Self.index.Add(stream.filename,stream)
+					
+					End
+				End
+			End
+		Else
+'			SaveState("") '********************************** fix this *********************************************8
+		End
+	End
+	
+	Method New()
+		Self.LoadAll()
+	End
+	
+	Method FileExists:Bool(filename:String)
+		
+		filename=filename.ToLower()
+		
+		If(Self.index.Contains(filename))
+		
+			Return True
+		
+		Else
+		
+			Return False
+		
+		End
+	
+	End
+	
+	Method ReadFile:FileStream(filename:String)
+	
+		filename=filename.ToLower()
+	
+		Local f:FileStream
+	
+		f=Self.index.Get(filename)
+		f.fileptr=0
+	
+		Return f
+	
+	End
+	
+	Method WriteFile:FileStream(filename:String)
+	
+		Local f:FileStream= New FileStream()
+	
+		f.filename=filename.ToLower()
+	
+		f.fileptr=0
+	
+		Self.index.Add(f.filename.ToLower(),f)
+	
+		Return f
+	
+	End
+	
+	Method SaveAll:Void()
+	
+		Local f:FileStream
+	
+		Self.fileData=Self._header
+	
+		Self.fileData=Self.fileData+Self.IntToString(Self.index.Count())
+	
+		If(Self.index.Count()>0)
+	
+			For f = Eachin Self.index.Values 
+	
+				Self.fileData=Self.fileData+Self.IntToString(f.filename.Length)
+	
+				If(f.filename.Length>0)
+	
+					Self.fileData=Self.fileData+f.filename
+	
+				End
+	
+				Self.fileData=Self.fileData+Self.IntToString(f.data.Length)
+	
+				If(f.data.Length>0)
+	
+					Self.fileData=Self.fileData+f.data
+	
+				End
+	
+			End
+	
+		End
+	
+		' SaveState(Self.fileData) '************************* fix ***************************
+	End
+End
+
+Class FileStream Extends DataConversion
+	
+	Field filename:String=""
+	
+	Field data:String=""
+	
+	Field fileptr:Int=0
+	
+	Method ReadString:String()
+		
+		Local result:String=""
+		
+		Local strLen:Int=Int(Self.data.Mid(Self.fileptr,3)) '[Self.fileptr..Self.fileptr+3])
+		
+		Self.fileptr+=3
+		
+		If(strLen>0)
+		
+			result=Self.data.Mid(Self.fileptr,strLen) '[Self.fileptr..Self.fileptr+strLen]
+		
+			Self.fileptr+=strLen
+		
+		End
+		
+		Return result
+	
+	End
+	
+	Method ReadInt:Int()
+	
+		Local result:String=""
+	
+		result=Self.data.Mid(Self.fileptr,3) '[Self.fileptr..Self.fileptr+3]
+	
+		Self.fileptr+=3
+	
+		Return Int(result)
+	
+	End
+	
+	Method WriteString:Void(val:String)
+	
+		Self.data=Self.data+Self.IntToString(val.Length)
+	
+		If(val.Length>0)
+	
+			Self.data=Self.data+val
+	
+		End
+	
+	End
+	
+	Method WriteInt:Void(val:Int)
+	
+		Self.data=Self.data+Self.IntToString(val)
+	
+	End
+	
+End

+ 465 - 0
bananas/billiards/PoolMod/Font.monkey2

@@ -0,0 +1,465 @@
+#Import "PoolMod"
+
+Private 
+Global __Afont:AngelFont
+Public
+
+Function LoadAngelFont:AngelFont(url:String)
+	Local font:AngelFont = New AngelFont()
+	font.italicSkew = 0.15
+	font.LoadFont(url)
+	Return font
+End Function	
+
+Function SetAngelFont:Void(font:AngelFont)
+		__Afont = font
+End Function
+
+Function GetActiveFont:AngelFont()
+	Return __Afont
+End
+
+Function ATextWidth:Int(str:String)
+	Return __Afont.TextWidth(str)
+End Function
+
+Function AFontHeight:Int()
+	If __Afont = Null  RuntimeError("font not initialized")
+	Return __Afont.height
+End function	
+
+Function ATextHeight:Int(str:String)
+	Return __Afont.TextHeight(str)
+End Function
+
+	
+Function RenderText:Void(canvas:Canvas,txt:String,x:Float,y:Float)
+	If __Afont = Null  RuntimeError("font not initialized")
+	__Afont.RenderText(canvas,txt,x,y)
+End Function
+
+Function GetChars:Char[]()
+	If __Afont = Null RuntimeError("font not initialized")
+	Return __Afont.chars
+End Function
+
+
+Class Char
+	Field asc:Int
+	Field x:Int
+	Field y:Int
+	
+	Field width:Int
+	Field height:Int = 0
+	
+	Field xOffset:Int = 0
+	Field yOffset:Int = 0
+	Field xAdvance:Int = 0
+	
+	
+	Method New(x:Int,y:Int, w:Int, h:Int, xoff:Int=0, yoff:Int=0, xadv:Int=0)
+		Self.x = x
+		Self.y = y
+		Self.width = w
+		Self.height = h
+		
+		Self.xOffset = xoff
+		Self.yOffset = yoff
+		Self.xAdvance = xadv
+	End
+	
+	Method Render(canvas:Canvas,fontImage:Image, linex:float,liney:Float)
+		canvas.DrawRect(linex+xOffset,liney+yOffset,width,height,fontImage, x,y)
+	End Method
+
+	Method toString:String()
+		Return String.FromChar(asc)+"="+asc
+	End Method
+	
+End Class
+
+Class KernPair
+	Field first:String
+	Field second:String
+	Field amount:Int
+	
+	
+	Method New(first:Int, second:Int, amount:Int)
+		Self.first = first
+		Self.second = second
+		Self.amount = amount
+	End
+#Rem		
+	Method toString:String()
+		Return "first="+String.FromChar(first)+" second="+String.FromChar(second)+" amount="+amount
+	End Method
+#End
+End Class
+
+Class AngelFont
+	Private
+	
+	field _list:StringMap<AngelFont> = New StringMap<AngelFont>
+	
+	Field image:Image	
+	Field chars:Char[] = New Char[256]
+	Field kernPairs:StringMap<KernPair> = New StringMap<KernPair>
+	Field iniText:String
+
+	Field xOffset:Int
+	Field yOffset:Int
+	
+	Field prevMouseDown:Bool = False
+
+	Public
+	Const ALIGN_LEFT:Int = 0
+	Const ALIGN_CENTER:Int = 1
+	Const ALIGN_RIGHT:Int = 2
+	
+	Global error:String
+	
+	Field name:String
+	Field useKerning:Bool = True
+
+	Field lineGap:Int = 5
+	Field height:Int = 0
+	Field heightOffset:Int = 9999
+	Field scrollY:Int = 0
+	
+	Field italicSkew:Float = 0.25
+	
+	Method New(url:String="")
+		If url <> ""
+			Self.LoadFont(url)
+			Self.name = url
+			_list.Add(url,Self)
+		Endif
+	End Method
+	
+	Method GetChars:Char[]()
+		Return chars
+	End
+
+	Method LoadFont:Void(url:String)
+		
+		error = ""
+		iniText = LoadString(url+".txt")
+		Local lines:= iniText.Split(String.FromChar(10))
+		For Local line:= Eachin lines
+		
+			line=line.Trim()
+			
+			If line.StartsWith("id,") Or line = "" Continue
+			If line.StartsWith("first,")
+				Continue
+			Endif
+			Local data:string[] = line.Split(",")
+			
+			For Local i:=0 Until data.Length
+				data[i]=data[i].Trim()
+			Next
+			
+			error += data.Length+","	'+"-"+line
+			If data.Length > 0
+				If data.Length = 3
+					kernPairs.Add(String.FromChar(Int(data[0]))+"_"+String.FromChar(Int(data[1])), New KernPair(Int(data[0]), Int(data[1]), Int(data[2])))
+				Else
+					If data.Length >= 8
+						chars[Int(data[0])] = New Char(Int(data[1]), Int(data[2]), Int(data[3]), Int(data[4]),  Int(data[5]),  Int(data[6]),  Int(data[7]))
+						Local ch := chars[Int(data[0])]
+						If ch.height > Self.height Self.height = ch.height
+						If ch.yOffset < Self.heightOffset Self.heightOffset = ch.yOffset
+					Endif
+				Endif
+			Endif
+		Next
+		
+		image = Image.Load(url+".png")
+	End Method
+	
+	Method RenderItalic:Void(canvas:Canvas,txt:string,x:Float,y:Float)
+		Local th:float=TextHeight(txt)
+		
+		canvas.PushMatrix()
+		'Transform 1,0,-italicSkew,1, x+th*italicSkew,y
+		RenderText(canvas,txt,0,0)
+		canvas.PopMatrix()		
+	End 
+	
+	Method RenderBold:Void(canvas:Canvas,txt:String, x:Int, y:Int)
+		RenderText(canvas,txt, x,y)
+		RenderText(canvas,txt, x+1,y)
+	End
+	
+	
+	Method RenderText:Void(canvas:Canvas,txt:String, x:Int, y:Int)
+		Local prevChar:String = ""
+		xOffset = 0
+		
+		For Local i:= 0 Until txt.Length
+			Local asc:Int = txt[i]
+			Local ac:Char = chars[asc]
+			Local thisChar:String = String.FromChar(asc)
+			If ac  <> Null
+				If useKerning
+					Local key:String = prevChar+"_"+thisChar
+					If kernPairs.Contains(key)
+						xOffset += kernPairs.Get(key).amount
+					Endif
+				Endif
+				ac.Render(canvas,image, x+xOffset,y)
+				xOffset += ac.xAdvance
+				prevChar = thisChar
+			Endif
+		Next
+	End Method
+	
+	Method RenderText:Void(canvas:Canvas,txt:String, x:Int, y:Int, align:Int)
+		xOffset = 0
+		Select align
+			Case ALIGN_CENTER
+				RenderText(canvas,txt,x-(TextWidth(txt)/2),y)
+			Case ALIGN_RIGHT
+				RenderText(canvas,txt,x-TextWidth(txt),y)
+			Case ALIGN_LEFT
+				RenderText(canvas,txt,x,y)
+		End Select
+	End Method
+
+	Method RenderHTML:Void(canvas:Canvas,txt:String, x:Int, y:Int)
+		Local prevChar:String = ""
+		xOffset = 0
+		Local italic:Bool = False
+		Local bold:Bool = False
+		Local th:Float=TextHeight(txt)
+		
+		For Local i:= 0 Until txt.Length
+			While txt.Mid(i,1) = "<" '[i..i+1] = "<"
+				Select txt.Mid(i+1,2) '[i+1..i+3]
+					Case "i>"
+						italic = True
+						i += 3
+					Case "b>"
+						bold = True
+						i += 3
+					Default
+						Select txt.Mid(i+1,3) '[i+1..i+4]
+							Case "/i>"
+								italic = False
+								i += 4
+							Case "/b>"
+								bold = False
+								i += 4
+						End
+				End
+				If i >= txt.Length
+					Return
+				End
+			Wend
+			Local asc:Int = txt[i]
+			Local ac:Char = chars[asc]
+			Local thisChar:String = String.FromChar(asc)
+			If ac  <> Null
+				If useKerning
+					Local key:String = prevChar+"_"+thisChar
+					If kernPairs.Contains(key)
+						xOffset += kernPairs.Get(key).amount
+					Endif
+				Endif
+				If italic = False
+					ac.Render(canvas,image, x+xOffset,y)
+					If bold
+						ac.Render(canvas,image, x+xOffset+1,y)
+					End
+				Else
+					canvas.PushMatrix()
+						'Transform 1,0,-italicSkew,1, (x+xOffset)+th*italicSkew,y
+						ac.Render(canvas,image, 0,0)
+						If bold
+							ac.Render(canvas,image, 1,0)
+						Endif					
+					canvas.PopMatrix()		
+				End	
+				xOffset += ac.xAdvance
+				prevChar = thisChar
+			Endif
+		Next
+	End Method
+	
+	Method RenderHTML:Void(canvas:Canvas,txt:String, x:Int, y:Int, align:Int)
+		xOffset = 0
+		Select align
+			Case ALIGN_CENTER
+				RenderHTML(canvas,txt,x-(TextWidth(StripHTML(txt))/2),y)
+			Case ALIGN_RIGHT
+				RenderHTML(canvas,txt,x-TextWidth(StripHTML(txt)),y)
+			Case ALIGN_LEFT
+				RenderHTML(canvas,txt,x,y)
+		End Select
+	End Method
+	
+	Function StripHTML:String(txt:String)
+		Local plainText:String = txt.Replace("</","<")
+		plainText = plainText.Replace("<b>","")
+		Return plainText.Replace("<i>","")
+	End
+
+	Method TextWidth:Int(txt:String)
+		Local prevChar:String = ""
+		Local width:Int = 0
+		For Local i:= 0 Until txt.Length
+			Local asc:Int = txt[i]
+			Local ac:Char = chars[asc]
+			Local thisChar:String = String.FromChar(asc)
+			If ac  <> Null
+				If useKerning
+					Local key:String = prevChar+"_"+thisChar
+					If kernPairs.Contains(key)
+						width += kernPairs.Get(key).amount
+					Endif
+				Endif
+				width += ac.xAdvance
+				prevChar = thisChar
+			Endif
+		Next
+		Return width
+	End Method
+	
+	Method TextHeight:Int(txt:String)
+		Local h:Int = 0
+		For Local i:= 0 Until txt.Length
+			Local asc:Int = txt[i]
+			Local ac:Char = chars[asc]
+			If ac.height+ac.yOffset > h h = ac.height+ac.yOffset
+		Next
+		Return h
+	End
+
+End Class
+
+Global Text:BitmapText
+
+Class BitmapText
+	
+	Field chWidth:Int
+	Field chHeight:Int
+
+	Field count:Int
+		
+	Field image:Image[]
+	
+	Field upper:Int
+	
+	Method New(image:Image[],count:Int)
+		Self.chWidth = image[0].Width
+		Self.chHeight = image[0].Height
+		Self.image = image
+		Self.count = count
+	End Method
+
+	Method CharWidth:Int()
+		Return chWidth
+	End Method
+	
+	Method CharHeight:Int()
+		Return chHeight
+	End Method
+	
+	Method Width:Int(str:String)
+		Return str.Length * chWidth
+	End Method
+	
+	Method Height:Int()
+		Return chHeight
+	End Method
+	
+	Method Draw:Void(canvas:Canvas,str:String,x:Int,y:Int)
+		Local tx:Int = x
+		Local ty:Int = y
+		For Local c:Int = Eachin str
+			c &= $FF
+			If c < 91
+				c -= 32
+			Else
+				c -= 64
+			Endif
+			canvas.DrawImage(image[c],tx,ty)
+			tx += chWidth
+		Next
+	End Method
+
+End Class
+
+Global LargeText:VaryFont
+
+Class VaryFont
+	Field fontImg:Image[]
+	
+	Const COUNT:Int = 94
+	
+						
+	Global param:Int[][] = New Int[][](New Int[]( 12,355,29,45),New int[]( 41,355,29,30),New Int[]( 70,355,29,47),New Int[](100,355,24,47),New Int[](128,355,29,47),New Int[](157,355,30,47),
+									   New Int[](193,355,15,28),New Int[](222,355,19,48),New Int[](247,355,19,48),New Int[](276,355,22,32),New Int[](302,355,28,39),New Int[](337,355,16,49),
+									   New Int[](363,355,23,34),New Int[](394,355,16,44),New int[](419,355,25,44),New int[](447,355,28,44),New int[](477,355,26,44),New int[](506,355,25,44),
+									   New Int[](536,355,24,44),New int[](563,355,27,44),New int[](593,355,25,44),New int[](622,355,27,44),New int[](651,355,26,44),New int[](679,355,27,44),
+									   New int[](708,355,26,44),New int[](744,355,15,44),New int[](773,355,15,51),New int[](796,355,25,38),New int[](825,355,26,38),New int[](855,355,24,38),
+									   New int[](885,355,23,44),New int[](910,355,30,47),New int[](939,355,31,44),New int[](970,355,27,44),New int[]( 12,403,27,44),New int[]( 42,403,27,44),
+									   New int[]( 72,403,24,44),New int[](100,403,25,44),New int[](128,403,27,44),New int[](158,403,26,44),New int[](189,403,23,44),New int[](216,403,23,44),
+									   New Int[](245,403,29,44),New Int[](275,403,25,44),New Int[](302,403,27,44),New Int[](332,403,27,44),New Int[](360,403,27,44),New Int[](391,403,26,44),
+									   New Int[](417,403,29,46),New Int[](448,403,28,44),New Int[](477,403,25,44),New Int[](505,403,29,44),New Int[](535,403,26,44),New Int[](562,403,30,44),
+									   New Int[](591,403,30,44),New Int[](621,403,29,44),New Int[](650,403,28,44),New Int[](680,403,27,44),New Int[](714,403,20,47),New Int[](739,403,24,44),
+									   New Int[](768,403,19,47),New Int[](794,403,29,26),New Int[](822,426,32,48),New Int[](857,403,18,20),New Int[](883,403,26,44),New Int[](912,403,27,44),
+									   New Int[](940,403,25,44),New Int[](970,403,26,44),New Int[]( 12,449,27,44),New Int[]( 43,449,24,44),New Int[]( 71,449,26,49),New Int[](100,449,26,44),
+						   			   New Int[](131,449,18,44),New Int[](159,449,20,49),New Int[](187,449,27,44),New Int[](217,449,24,44),New Int[](245,449,28,44),New Int[](274,449,26,44),
+									   New Int[](303,449,27,44),New Int[](332,449,26,49),New Int[](361,449,26,49),New Int[](392,449,24,44),New Int[](421,449,24,44),New Int[](450,449,24,44),
+									   New Int[](479,449,24,44),New Int[](506,449,26,44),New Int[](534,449,29,44),New Int[](563,449,27,44),New Int[](592,449,27,49),New Int[](622,449,25,44),
+									   New Int[](654,449,21,48),New Int[](686,449,13,48),New Int[](711,449,21,48),New Int[](735,449,31,34))
+
+
+	Method New(image:Image)
+		fontImg = New Image[COUNT]
+		For Local i:Int = 0 Until COUNT
+			fontImg[i] = New Image(image,param[i][0],param[i][1],param[i][2],param[i][3])
+		Next
+	End Method
+	
+	Method TextWidth:Int(str:String)
+		Local l:Int= 0	
+		For Local n:Int = Eachin str
+			Local i:Int = GetIndex(n)
+			If i > -1
+				l += param[i][2]
+			Else
+				l += 20
+			Endif
+		Next
+		Return l
+	End Method
+	
+	Method TextHeight:Int(str:String)
+		Local l:Int = 0
+		If str > 0
+			l = param[0][3]
+		Endif
+		Return l
+	End Method
+
+	Method GetIndex:Int(n:Int)
+			If n >32 And n < 128 Then Return n-33
+			Return -1
+	End Method	
+
+	Method Draw:Void(canvas:Canvas,str:String,x:Float,y:Float)
+		If str.Length = 0 Return
+		For Local n:Int = Eachin str
+			Local i:Int = GetIndex(n)
+			If i > -1
+				canvas.DrawImage(fontImg[i],x,y)
+				x += param[i][2]
+			Else
+				x += 20
+			Endif
+		Next
+	End Method
+
+End Class

+ 717 - 0
bananas/billiards/PoolMod/Gui.monkey2

@@ -0,0 +1,717 @@
+#Import "PoolMod"
+
+#Import "data/frame.png"
+#Import "data/button.png"
+
+Global __atlas:Image
+Global __btnAtlas:Image
+
+Function GetAtlas:Image()
+	If __atlas = Null
+		__atlas = Image.Load("asset::frame.png")
+	Endif
+	Return __atlas
+End Function
+
+Function GetBtnAtlas:Image()
+	If __btnAtlas = Null
+		__btnAtlas = Image.Load("asset::button.png")	
+	Endif
+	Return __btnAtlas
+End Function
+
+Class Button
+	Field pos:PVector2D
+	Field x:Float
+	Field y:Float
+	Field width:Float
+	Field height:Float
+	Field text:String
+	Field offx:Float
+	Field offy:Float
+	Field activated:Int
+	Field selected:Int
+	Field oldDown:Int
+	Field image:Image[]
+	Field font:AngelFont
+
+	Method New(font:AngelFont,img:Image[],x:Int,y:Int,str:String,p:PVector2D=Null)
+		Self.font= font
+		If p = Null
+			Self.pos = New PVector2D(x,y)
+			Self.x = 0
+			Self.y = 0
+		Else
+			pos = p
+			Self.x = x
+			Self.y = y
+		End If
+		Self.text = str
+		Self.width = img[0].Width
+		Self.height = img[0].Height
+		Self.offx = offx
+		Self.offy = offy
+		Self.image = img
+		Self.SetText(str)
+	End Method
+	
+	Method SetText:Void(str:String)
+		text = str
+		If text.Length > 0
+			offx = (image[0].Width - font.TextWidth(str))/2.0
+			offy = (image[0].Height - font.TextHeight(str))/2.0
+		Endif
+	End Method
+	
+	Method Update:Void()
+		Local thisDown := Mouse.ButtonPressed(MouseButton.Left)
+		If thisDown
+			If Not oldDown And inArea()
+				selected = True
+			Endif
+		Elseif oldDown
+			If selected = True
+				If activated = False
+					If inArea()
+						activated = True
+						selected = False
+					Else
+						selected = False
+					Endif
+				Endif
+			Elseif activated
+				activated = False
+			Endif
+		Else
+			activated = False
+		Endif
+		oldDown = thisDown
+	End Method
+	
+	Method GetState:Int()
+		Local state:Int = activated
+		activated = False
+		Return state
+	End Method
+
+	Method inArea:Int()
+		Local tx := Mouse.X
+		Local ty := Mouse.Y
+		If tx < pos.x+x Return False
+		If ty < pos.y+y Return False
+		If tx > pos.x+x+width Return False
+		If ty > pos.y+y+height Return False
+		Return True	
+	End Method
+
+	Method Render:Void(canvas:Canvas)
+		Local index:Int = 0
+		If selected Or inArea() Then index = 1
+		canvas.Color = New Color(1,1,1)
+		canvas.DrawImage(image[index],pos.x+x,pos.y+y)
+		If text.Length > 0
+			font.RenderText(canvas,text,pos.x+x+offx+index,pos.y+y+offy+index)	
+		Endif
+	End Method
+
+
+End Class
+
+
+Class Button2 
+	Field pos:PVector2D=Null
+	Field x:Float
+	Field y:Float
+	Field text:String=""
+	Field width:Float
+	Field height:Float
+	Field offx:Float
+	Field offy:Float
+	Field image:Image[]
+
+	Field selected:Int=0
+	Field activated:Int=0
+	
+	Field oldDown:Int=0
+
+	Method SetText:Void(tstr:String)
+		text=tstr
+		Local tw:Int=tstr.Length*Text.CharWidth()
+		offx=(image[0].Width-tw)/2.0
+		Local th:Int=image[0].Height-Text.CharHeight()
+		offy=th/2.0
+	End Method
+
+	Method New(timg:Image[],tx:Int,ty:Int,tstr:String,tp:PVector2D=Null)
+		If(tp=Null)
+			pos=New PVector2D
+			pos.x=tx
+			pos.y=ty
+			x=0.0
+			y=0.0
+		Else
+			pos=tp
+			x=(tx)
+			y=(ty)
+		Endif
+		text=tstr
+		width=timg[0].Width
+		height=timg[0].Height
+		offx=offx
+		offy=offy
+		image=timg
+		SetText(tstr)
+		
+	End Method
+	
+	Method inArea:Int()
+	
+		Local ttx:Float=Mouse.X
+		Local tty:Float=Mouse.Y
+		If ttx<pos.x+x Return 0
+		If tty<pos.y+y	Return 0
+		If ttx>pos.x+x+width Return 0
+		If tty>pos.y+y+height Return 0
+		Return 1
+		 
+	End Method
+	
+	Method Update:Void()
+
+		Local SelfDown:Int=Mouse.ButtonDown(MouseButton.Left)
+
+		If SelfDown
+			If Not(oldDown) And inArea()
+				selected=1
+			End
+		Elseif oldDown
+			If selected=1
+				If activated=0
+					If inArea()
+						activated=1
+						selected=0
+					Else
+						selected=0
+					End
+				End
+			Else
+				If activated activated = 0
+			End
+		End
+		oldDown=SelfDown
+	End Method
+
+	Method GetState:Int()
+		Local tstate:Int=activated
+		activated=0
+		Return tstate
+	End Method
+
+	Method Render:Void(canvas:Canvas)
+		Local index:Int=0
+		If selected	index=1
+		canvas.Color = New Color(1,1,1)
+		canvas.DrawImage(image[index],pos.x+x,pos.y+y)
+		Text.Draw(canvas,text,pos.x+x+offx+index,pos.y+y+offy+index)
+	End Method
+	
+End Class
+
+Class FrameAnimation
+
+	Field x					:Float
+	Field y					:Float
+	
+	Field px				:Int
+	Field py				:Int
+	
+	Field width				:Int
+	Field height			:Int
+	
+	Field delay				:Int
+	Field currentWidth		:Int
+	Field currentHeight		:Int
+	
+	Field stp				:Int
+	Field time				:Int
+	
+	Field animating			:Int
+	
+	Field frameTopImg		:Image[]
+	Field frameBotImg		:Image[]
+	Field topImg			:Image
+	Field botImg			:Image
+	Field lrImg				:Image[]
+	Field rectImg			:Image
+	
+	Method New(media:Media,x:Int,y:Int,width:Int,height:Int,delay:Int = 2,stp:Int = 40)
+		Self.x = x
+		Self.y = y
+		Self.width = width
+		Self.height = height
+		Self.delay = delay
+		Self.currentWidth = 1
+		Self.currentHeight = 1
+		Self.stp = stp
+		Self.frameTopImg = media.frameTopImg
+		Self.frameBotImg = media.frameBotImg
+		Self.topImg =      media.topImg
+		Self.botImg =      media.botImg
+		Self.lrImg =       media.lrImg
+		Self.rectImg =	   media.rectImg
+	End Method
+	
+	Method Init:Void()
+		animating = True
+		currentWidth = 1
+		currentHeight = 1
+		px = x+width/2
+		py = y+height/2
+		time = Millisecs()
+	End Method
+	
+	Method Update:Int()
+		If animating = True
+			If Millisecs() > time+delay
+				currentWidth += stp		
+				currentHeight += stp
+				px -= stp/2
+				py -= stp/2
+				If currentWidth > width  Then 
+					currentWidth = width 
+					px = x
+				Endif
+				If currentHeight > height Then
+					currentHeight = height 
+					py = y
+				Endif
+				If currentHeight = height And currentWidth = width 
+					animating = False
+				Endif
+				time = Millisecs()
+			Endif
+		Endif
+		Return animating
+		
+	End Method
+		 
+
+	Method Render:Void(canvas:Canvas)
+
+		Local tw:Int = 4
+		Local th:Int = 4
+		Local fx:Float = px-tw
+		Local fy:Float = py-th
+		
+		canvas.DrawImage(frameTopImg[0],fx,fy)
+		canvas.DrawImage(topImg,fx+tw,fy,0,currentWidth,1)
+		canvas.DrawImage(frameTopImg[0],fx+currentWidth+tw,fy)
+		
+		canvas.DrawImage(lrImg[0],fx,fy+th,0,1,currentHeight)
+		canvas.DrawImage(lrImg[1],fx+tw+currentWidth,fy+th,0,1,currentHeight)
+		
+		canvas.DrawImage(frameBotImg[0],fx,fy+th+currentHeight)
+		canvas.DrawImage(botImg,fx+tw,fy+th+currentHeight,0,currentWidth,1)
+		canvas.DrawImage(frameBotImg[1],fx+tw+currentWidth,fy+th+currentHeight)
+		canvas.DrawImage(rectImg,px,py,0,currentWidth,currentHeight)		
+	End Method
+
+End Class
+
+Class GuiFrame
+	Field x:Float
+	Field y:Float
+	Field lft:Float
+	Field top:Float
+	Field right:Float
+	Field bottom:Float
+	Field width:Float
+	Field height:Float
+	Field color:Color
+	
+	Field leftTopImg:Image
+	Field leftBotImg:Image
+	Field rightTopImg:Image
+	Field rightBotImg:Image
+	
+	Field topImg:Image
+	Field botImg:Image
+	Field leftImg:Image
+	Field rightImg:Image
+	Field areaImg:Image
+	
+	Method New(x:Float,y:Float,width:Float,height:Float,color:Color = New Color(1,1,1))
+		Self.x = x
+		Self.y = y
+		Self.width = width
+		Self.height = height
+		Self.color = color
+		
+		Local atlas:Image = GetAtlas()
+		Self.leftTopImg = New Image(atlas,0, 0,16,16)
+		Self.leftBotImg = new Image(atlas,0,16,16,16)
+		Self.rightTopImg = new Image(atlas,16,0,16,16)
+		Self.rightBotImg = new Image(atlas,16,16,16,16)
+		
+		Self.topImg = new Image(atlas,15, 0, 1,16)
+		Self.botImg = new Image(atlas,15,16, 1,16)
+		Self.leftImg = new Image(atlas,0,15,16, 1)
+		Self.rightImg = new Image(atlas,16,15,16,1)
+		Self.areaImg = new Image(atlas,16,16,1,1)
+		
+		Self.lft = x - 8
+		Self.top = y - 8
+		Self.bottom = y+height
+		Self.right  = x+width
+		
+	End Method
+	
+	Method Setxy:Void(x:Float,y:Float)
+
+		Self.x = x
+		Self.y = y
+		Self.lft = x - 8
+		Self.top = y - 8
+		Self.bottom = y+height
+		Self.right  = x+width
+	
+	End Method
+	
+	Method SetArea:Void(w:Float,h:Float)
+		width = w
+		height = h
+		bottom = y + height
+		right = x + width
+	End Method
+	
+	Method Render:Void(canvas:Canvas)
+		
+		Local oldColor:Color = canvas.Color
+		canvas.Color = color
+		
+		canvas.DrawImage(leftTopImg, lft, top,0,.5,.5)
+		canvas.DrawImage(rightTopImg, right, top,0,0.5,0.5)
+		canvas.DrawImage(leftBotImg, lft, bottom,0,0.5,0.5)
+		canvas.DrawImage(rightBotImg, right, bottom,0,0.5,0.5)
+		
+		canvas.DrawImage(topImg, x, top,0,width,.5)
+		canvas.DrawImage(botImg, x, bottom,0,width,.5)
+		canvas.DrawImage(leftImg,lft,y,0,.5,height)
+		canvas.DrawImage(rightImg,right,y,0,.5,height)
+		canvas.DrawImage(areaImg,x,y,0,width,height)
+		canvas.Color = oldColor
+	End Method
+
+End Class
+
+Class Enquiry
+
+	Field x:Int
+	Field y:Int
+	Field frame:GuiFrame
+	Field txtBox:GuiTextBox
+	Field okBtn:Button
+	Field cancelBtn:Button
+	Field startBtn:Button
+
+	Method New(font:AngelFont,txt:String,x:Int,y:Int,width:Int,height:Int)
+		frame = New GuiFrame(x-5,y-5,width+5,height+5,New Color($44/255.0,$44/255.0,$AA/255.0))
+		txtBox = New GuiTextBox(txt,x,y,width,height)
+		Local btnImage:Image[] = LoadFrames(GetBtnAtlas(),0,62,120,62,2)
+		okBtn = New Button(font,btnImage,x,y+height-70,"OK")
+		cancelBtn = New Button(font,btnImage,x+130,y+height - 70,"Cancel")
+		startBtn = New Button(font,btnImage,x+260,y+height - 70,"Restart")
+	End Method
+	
+	Method Update:Void()
+		okBtn.Update()
+		cancelBtn.Update()
+		startBtn.Update()
+	End Method
+	
+	Method IsCanceled:Int()
+		Return cancelBtn.GetState() = True
+	End Method
+	
+	Method IsOK:Int()
+		Return okBtn.GetState() = True
+	End Method
+	
+	Method IsRestart:Int()
+		Return startBtn.GetState() = True
+	End Method
+	
+	Method Render:Void(canvas:Canvas)
+		frame.Render(canvas)
+		txtBox.Render(canvas)
+		okBtn.Render(canvas)
+		cancelBtn.Render(canvas)
+		startBtn.Render(canvas)
+	End Method
+End Class
+
+Class StringLine
+	Field x:Int
+	Field y:Int
+	Field txt:String
+	
+	Method New(txt:String,x:Int,y:Int)
+		Self.txt = txt
+		Self.x = x
+		Self.y = y
+	End Method
+	
+	Method Render:Void(canvas:Canvas)
+		RenderText(canvas,txt,x,y)
+	End Method
+End Class
+
+Class TextFieldGui
+	Field pos:PVector2D
+	Field x:Int
+	Field y:Int
+	Field image:Image
+	Field cancelBtn:Button2
+	Field okBtn:Button2
+	Field textField:TextField
+	
+	Method New(x:Int,y:Int,txt:String,p:PVector2D,blinkDelay:Int)
+		If( Not p)
+			Self.pos=New PVector2D()
+			Self.pos.x=(x)
+			Self.pos.y=(y)
+			Self.x=0
+			Self.y=0
+		Else
+			Self.pos=p
+			Self.x=x
+			Self.y=y
+		End
+		image=media.getNameImg
+		cancelBtn=New Button2(media.smallBtnImg,50,110,"Cancel",Self.pos)
+		okBtn=New Button2(media.smallBtnImg,200,110,"Ok",Self.pos)
+		textField=New TextField(30.0,72.0,txt,15,Null,Self.pos,blinkDelay)
+	End
+	
+	Method Update:Int()
+		textField.Update()
+		cancelBtn.Update()
+		okBtn.Update()
+		If cancelBtn.GetState()
+			Return -1
+		ElseIf okBtn.GetState()
+			If textField.GetText()=""
+				Return 1
+			End
+			Return 0
+		End
+		Return 1
+	End
+	
+	Method GetText:String()
+		Return Self.textField.GetText()
+	End
+	
+	Method Render:Void(canvas:Canvas)
+		canvas.DrawImage(Self.image,Self.pos.x+(Self.x),Self.pos.y+(Self.y),0)
+		textField.Render(canvas)
+		cancelBtn.Render(canvas)
+		okBtn.Render(canvas)
+	End
+End
+
+Class GuiTextBox
+	
+	Field yOffset:Int = 0	
+	Field slist:List<StringLine>
+	Field align:Int
+	
+	Const ALIGN:Int = AngelFont.ALIGN_LEFT
+	Const lineGap:Int = 5
+	
+	Method New(text:String,x:Int,y:Int,width:Int,alignment:Int = ALIGN)
+		slist = New List<StringLine>
+		Local thisLine:String = ""
+		Local charOffset:Int = 0
+		
+		Local wordLen:Int = 0
+		Local word:String = ""
+		
+		align = alignment
+		
+		yOffset = 0
+		For Local i := 0 Until text.Length
+			If y+yOffset > DEVICE_HEIGHT
+				Return
+			Endif		
+		
+			Local asc:Int = text[i]
+			Select asc
+				Case 32	' space
+					wordLen = ATextWidth(word)
+					If charOffset + wordLen > width
+						slist.AddLast(New StringLine(thisLine,x,y+yOffset))
+						yOffset += lineGap+AFontHeight()
+						thisLine = ""
+						charOffset = 0
+					Endif
+					charOffset += wordLen+GetChars()[32].xAdvance
+					thisLine += word + " "
+					
+					word = ""
+				Case 10' newline
+					wordLen = ATextWidth(word)
+					If charOffset + wordLen > width
+						slist.AddLast(New StringLine(thisLine,x,y+yOffset))
+						yOffset += lineGap+AFontHeight()
+						thisLine = ""
+					Endif
+					thisLine += word
+				
+					slist.AddLast(New StringLine(thisLine,x,y+yOffset))
+					yOffset += lineGap+AFontHeight()
+					thisLine = ""
+					charOffset = 0
+					word = ""
+				Default
+					Local ch:Char = GetChars()[asc]
+					word += String.FromChar(asc)
+			End Select
+		Next
+
+		If word <> ""
+			wordLen = ATextWidth(word)
+			If charOffset + wordLen > width
+				slist.AddLast(New StringLine(thisLine,x,y+yOffset))
+				yOffset += lineGap+AFontHeight()
+				thisLine = ""
+			Endif
+			thisLine += word
+		Endif
+		If thisLine <> ""
+			slist.AddLast(New StringLine(thisLine,x,y+yOffset))
+			yOffset += lineGap+AFontHeight()
+		Endif
+	End Method
+		
+	Method Render:Void(canvas:Canvas)
+		Local node:List<StringLine>.Node = slist.FirstNode()
+		canvas.Color = New Color(1,1,0)
+		While node.Value
+			node.Value.Render(canvas)
+			node = node.Succ
+		Wend
+		canvas.Color = New Color(1,1,1)	
+	End
+	
+End Class
+
+
+Class TextField
+	Field x:Int
+	Field y:Int
+	Field pos:PVector2D
+	Field text:String
+	Field maxChars:Int
+	Field cursorx:Int
+	Field textImg:Image[]
+	
+	Field blink			:Int
+	Field delay			:Int
+	Field time			:Int
+	
+	Method New(x:Float,y:Float,txt:String,maxChr:Int,txtImg:Image[]=Null,p:PVector2D= Null,blinkDelay:Int = 200)
+		If p = Null
+			pos = New PVector2D(x,y)
+			Self.x = 0
+			Self.y = 0
+		Else
+			Self.pos = p
+			Self.x = x
+			Self.y = y
+			
+		Endif
+		Self.maxChars = maxChr
+		If txtImg = Null 
+			Self.textImg = GetFont()
+		Else
+			Self.textImg = txtImg
+		Endif
+		
+		Self.delay = blinkDelay
+		Self.time = Millisecs()
+		Self.text = txt
+		If text.Length > maxChr text = text.Left(maxChr)
+		cursorx = text.Length
+	End Method
+	
+	Method SetFont:Void(image:Image[])
+		textImg = image
+	End Method
+	
+	Method GetPixelWidth:Int()
+		Return (maxChars+1)*textImg[0].Width
+	End Method
+	
+	Method GetText:String()
+		Return text
+	End Method
+	
+	Method Update:Void()
+		Local chr:Int = Keyboard.GetChar()
+		If chr>255 chr = Keyboard.TranslateKey(Cast<Key>(chr))
+		
+		If chr
+			Select chr
+				Case Key.Up
+				Case Key.Down
+				Case Key.Insert
+				Case Key.Tab
+				Case Key.Enter
+				Case Key.Escape			
+				Case Key.Backspace
+					Print cursorx
+					If cursorx > 0
+						text = text.Slice(0,cursorx-1)+text.Slice(cursorx,text.Length)
+						Print text
+						cursorx -= 1
+					Endif
+				Case Key.PageUp
+					cursorx = 0
+				Case Key.PageDown
+					cursorx = text.Length
+				Case Key.KeyEnd
+					cursorx = text.Length
+				Case Key.Home
+					cursorx = 0
+				Case Key.Left
+					If cursorx > 0 
+						cursorx -= 1
+					Endif
+				Case Key.Right
+					If cursorx < text.Length
+						cursorx += 1
+					Endif
+				Default
+					If text.Length < maxChars
+						text = text.Slice(0,cursorx)+String.FromChar(chr)+text.Slice(cursorx,text.Length)
+						cursorx +=1
+					Endif	
+			End Select
+		Endif
+		
+		If Millisecs() > time+delay
+			blink = Not blink
+			time = Millisecs()
+		Endif
+
+	End Method
+
+	Method Render:Void(canvas:Canvas)
+		Text.Draw(canvas,text,pos.x+x,pos.y+y)
+		If blink canvas.DrawRect(pos.x+x+Text.Width(text),pos.y+y+Text.Height(),8,3)
+	End Method
+		
+
+End Class
+

+ 226 - 0
bananas/billiards/PoolMod/Media.monkey2

@@ -0,0 +1,226 @@
+#Import "PoolMod"
+#Import "data/poolAtlasb.png"
+#Import "data/hit03.ogg"
+#Import "data/hit04.ogg"
+#Import "data/hit05.ogg"
+#Import "data/hit06.ogg"
+#Import "data/shoot.ogg"
+#Import "data/bank01.ogg"
+#Import "data/bank02.ogg"
+#Import "data/rackup.ogg"
+#Import "data/applaused.ogg"
+#Import "data/pocket1.ogg"
+#Import "data/fail.ogg"
+#Import "data/completion.ogg"
+#Import "data/beep.ogg"
+#Import "data/beep2.ogg"
+
+
+
+Global media:Media
+
+Class Media
+	Field titleImg:Image
+	Field tableImg:Image
+	Field railImg:Image
+	Field ballImg:Image
+	Field stickImg:Image[]
+	Field ghostImg:Image[]
+	Field arrowsImg:Image
+	Field rotatorImg:Image
+	Field buttonImg:Image[]
+	Field smallBtnImg:Image[]
+	Field stripImg:Image
+	
+	Field getNameImg:Image
+	
+	Field logoImg:Image
+	Field frameTopImg:Image[]
+	Field frameBotImg:Image[]
+	Field topImg:Image
+	Field botImg:Image
+	Field lrImg:Image[]
+	Field rectImg:Image
+	Field meterImg:Image
+	Field stretchImg:Image
+	Field sliderImg:Image
+	Field ballCol:Sound[] = New Sound[4]
+	Field shootCol:Sound
+	Field railCol:Sound[] = New Sound[2]
+	Field rackUp:Sound
+	Field applause:Sound
+	Field pocketCol:Sound
+	Field pocketChannel:Channel = New Channel
+	Field scratchSnd:Sound
+	Field completionSnd:Sound
+	Field beepSnd:Sound
+	Field beepBSnd:Sound
+	
+	Field arcImg:Image = Null
+	Field lineImg:Image = Null
+	Field channeltime:Int[] = New Int[8]
+	Field colChannel:Channel[] = New Channel[4]
+	Field colChanIndex:Int = 0
+	Field colLastChanIndex:Int = 3
+	Field colFirstChanIndex:Int = 0
+
+	Field scoreChannel:Channel[] = New Channel[5]
+	Field scoreChanIndex:Int = 0
+	Field scoreLastChan:Int = 5
+		
+	Method New()
+	
+	End Method
+	
+	Method New(atlasImg:Image)
+		If(atlasImg = Null) RuntimeError("unable to load GameAtlas.png")
+		titleImg = New Image(atlasImg,0,544,640,480)
+		tableImg = New Image(atlasImg,0,0,640,360)
+		railImg = New Image(atlasImg,640,0,384,41)
+		ballImg = New Image(atlasImg,996,104,23,23)
+		ballImg.Handle= New Vec2f(.4,.4)
+		stickImg = LoadFrames(atlasImg,1,501,392,9,1)
+		stickImg[0].Handle = New Vec2f(1.0,.5)
+		ghostImg = LoadFrames(atlasImg,640,140,22,22,3)
+		ghostImg[0].Handle = New Vec2f(.5,.5)
+		ghostImg[1].Handle = New Vec2f(.5,.5)
+		ghostImg[2].Handle = New Vec2f(.5,.5)
+		arrowsImg = New Image(atlasImg,890,141,47,47)
+		arrowsImg.Handle = New Vec2f(23.0/47.0,23.0/47.0)
+		rotatorImg = New Image(atlasImg,788,140,101,64)
+		rotatorImg.Handle = New Vec2f(51.0/101.0,43.0/64.0) 
+		buttonImg = LoadFrames (atlasImg,640,41,190,62,2)
+		smallBtnImg = LoadFrames(atlasImg,643,289,130,50,2)
+		stripImg = New Image(atlasImg,1015,130,1,3)
+		stripImg.Handle = New Vec2f(0.0,.5)
+		getNameImg = New Image(atlasImg,641,848,383,176)
+		logoImg = New Image(atlasImg,640,545,255,275)
+		frameTopImg = LoadFrames(atlasImg,997,128,4,4,2)
+		frameBotImg = LoadFrames(atlasImg,997,132,4,4,2)
+		topImg = New Image(atlasImg,1000,128,1,4)
+		botImg = New Image(atlasImg,1000,132,1,4)
+		lrImg = LoadFrames(atlasImg,997,131,4,1,2)
+		rectImg = New Image(atlasImg,1011,131,1,1)
+		meterImg = New Image(atlasImg,640,103,346,36)
+		stretchImg = New Image(atlasImg,988,103,1,36)
+		sliderImg = New Image(atlasImg,990,103,4,36)
+		ballCol[0] = Sound.Load("asset::hit03.ogg")
+		If ballCol[0] = Null RuntimeError("unable to load hit03.ogg")
+		ballCol[1] = Sound.Load("asset::hit04.ogg")
+		If ballCol[1] = Null RuntimeError("unable to load hti04.ogg")
+		ballCol[2] = Sound.Load("asset::hit05.ogg")
+		If ballCol[2]  =  Null RuntimeError("unable to lead hit05.ogg")
+		ballCol[3]  =  Sound.Load("asset::hit06.ogg")
+		If ballCol[3] = Null RuntimeError("unable to load hit06.ogg")
+		shootCol = Sound.Load("asset::shoot.ogg")
+		If shootCol = Null RuntimeError("unable to load shoot.ogg")
+		railCol[0] = Sound.Load("asset::bank01.ogg")
+		If railCol[0] = Null RuntimeError("unable to load bank01.ogg")
+		railCol[1] = Sound.Load("asset::bank02.ogg")
+		If railCol[1] = Null RuntimeError("unable to load bank02.ogg")
+		rackUp = Sound.Load("asset::rackup.ogg")
+		If rackUp = Null RuntimeError("unable To load rackUp.ogg")
+		applause = Sound.Load("asset::applaused.ogg")
+		If applause = Null RuntimeError("unable to load applaused.ogg")
+		pocketCol = Sound.Load("asset::pocket1.ogg")
+		If pocketCol = Null RuntimeError("unable to load pocket1.ogg")
+		scratchSnd = Sound.Load("asset::fail.ogg")
+		If scratchSnd = Null RuntimeError("unable to load fail.ogg")
+		completionSnd = Sound.Load("asset::completion.ogg")
+		If completionSnd = Null RuntimeError("unable to load completionSnd.ogg")
+		beepSnd = Sound.Load("asset::beep.ogg")
+		If beepSnd = Null Print("unable to load beep.ogg")
+		beepBSnd = Sound.Load("asset::beep2.ogg")
+		If beepBSnd  =  Null RuntimeError("unable to load beep2.ogg")
+		For Local i:Int = 0 Until 4
+			colChannel[i] = New Channel()
+		Next
+		
+		For Local i:Int = 0 Until scoreLastChan
+			scoreChannel[i] = New Channel
+		Next
+	End Method
+	
+	
+	Method UpdateChannel:Void()
+		colChanIndex += 1
+		If colChanIndex >= colLastChanIndex colChanIndex = colFirstChanIndex
+	End Method
+	
+	Method PlayPocketCol:Void()
+		
+		Local t:Int = Millisecs()-channeltime[colChanIndex]
+		If(t>200)
+			channeltime[colChanIndex] = Millisecs()
+			pocketCol.Play()
+			UpdateChannel()
+		Endif
+		
+	End Method
+	
+	Method PlayScratch:Void()
+		
+		scratchSnd.Play()
+		
+	End Method
+	
+	Method PlayBallCol:Void(n:Int)
+		
+		If(n>3) n = 3
+		If(n<0) n = 0
+		Local t:Int = Millisecs()-channeltime[colChanIndex]
+		If(t>200)
+			channeltime[colChanIndex] = Millisecs()
+			colChannel[colChanIndex].Play(ballCol[n])
+			UpdateChannel()
+		Endif
+		
+	End Method
+	
+	Method PlayRailCol:Void(n:Int)
+		
+		Local t:Int = Millisecs()-channeltime[colChanIndex]
+		If(t>200)
+			channeltime[colChanIndex] = Millisecs()
+			colChannel[colChanIndex].Play(railCol[n],0)
+			UpdateChannel()
+		Endif
+		
+	End Method
+	
+	Method updateScoreChan:Void()
+		
+		scoreChanIndex =(scoreChanIndex + 1) Mod scoreLastChan
+		
+	End Method
+	
+	Method PlayBeep:Void()
+		
+		Local t:Int = Millisecs()-channeltime[scoreChanIndex]
+		channeltime[scoreChanIndex] = Millisecs()
+		scoreChannel[scoreChanIndex].Play(beepSnd)
+		updateScoreChan()
+		
+	End Method
+	
+	Method PlayBeepB:Void()
+		
+		Local t:Int = Millisecs()-channeltime[scoreChanIndex]
+		channeltime[scoreChanIndex] = Millisecs()
+		scoreChannel[scoreChanIndex].Play(beepBSnd)
+		updateScoreChan()
+		
+	End Method
+	
+	Method PlayShootCol:Void()
+		Local t:Int = Millisecs()-channeltime[colChanIndex]
+		If(t>200)
+			channeltime[colChanIndex] = Millisecs()
+			colChannel[colChanIndex].Play(shootCol)
+			UpdateChannel()
+		End If
+		
+	End Method
+	
+End Class
+

+ 162 - 0
bananas/billiards/PoolMod/PlayerRecords.monkey2

@@ -0,0 +1,162 @@
+#Import "PoolMod"
+
+Class ScoreTable
+	Field fileName:String=""
+	Field recordList:RecordList
+	Field frame:FrameAnimation
+	Field fileHandler:FileSystem
+	Field stream:FileStream
+	Field down:Int=0
+	
+	Method New(fileName:String)
+		Self.fileName=fileName
+		Self.recordList=New RecordList()
+		Self.frame=New FrameAnimation(media,50,20,DEVICE_WIDTH-100,DEVICE_HEIGHT-40,2,40)
+		Self.fileHandler= New FileSystem()
+		If(Self.fileHandler.FileExists(fileName))
+			Self.stream=Self.fileHandler.ReadFile(fileName)
+			Repeat
+				Local name:String=Self.stream.ReadString()
+				If((name).Length<>0)
+					Local record:Record=New Record()
+					record.name=name
+					record.score=Self.stream.ReadInt()
+					record.mostBallSinRow=Self.stream.ReadInt()
+					record.mostPocketed=Self.stream.ReadInt()
+					record.mostCompletedSets=Self.stream.ReadInt()
+					Self.recordList.AddLast(record)
+				Else
+					Exit
+				End
+			Forever
+		End
+		Self.down=0
+	End
+
+	Field ready:Int=0
+	Method InitFrame:Void()
+		Self.frame.Init()
+		Self.ready=0
+	End
+	
+	Method SetRecord:Void(record:Record)
+		Self.recordList.AddLast(record.GetCopy())
+		Self.recordList.Sort(0)
+		While(Self.recordList.Count()>50)
+			Self.recordList.RemoveLast()
+		End
+	End
+	
+	Method SaveTable:Void()
+		Self.stream=Self.fileHandler.WriteFile(Self.fileName)
+		For Local record:Record = Eachin Self.recordList
+			Self.stream.WriteString(record.name)
+			Self.stream.WriteInt(record.score)
+			Self.stream.WriteInt(record.mostBallSinRow)
+			Self.stream.WriteInt(record.mostPocketed)
+			Self.stream.WriteInt(record.mostCompletedSets)
+		End
+		Self.fileHandler.SaveAll()
+	End
+	
+	Method Update:Bool()
+		Local done:Int=0
+		If(Self.ready=0)
+			If(Self.frame.Update()=0)
+				Self.ready=1
+			End
+		Else
+			If Mouse.ButtonDown(MouseButton.Left)
+				Self.down=1
+			End
+			If Mouse.ButtonDown(MouseButton.Left)=0 And Self.down=1
+				Self.down=0
+				done=1
+			End
+		End
+		Return Not done
+	End
+	
+	Method Render:Void(canvas:Canvas)
+		Self.frame.Render(canvas)
+		If(Self.ready=1)
+			Local x:Float=80.0
+			Local y:Float=130.0
+			Local n:Float=0.0
+			If Not recordList.Empty
+				Text.Draw(canvas,"                 TOP 10                        ",100,30)
+				Text.Draw(canvas,"  Name        score   Sets continuos pocketed  ",100,80)
+				Text.Draw(canvas,"========================",80,100)
+				For Local record:Record  = Eachin Self.recordList
+					record.Render(canvas,x,y+n*20.0)
+					n=n+1.0
+					If(n>10.0)
+						Exit
+					End
+				End
+			Else
+				Text.Draw(canvas,"Recored List is Empty",200,250)
+			End
+			Text.Draw(canvas,"               Press To Exit",80,400)
+		End
+	End
+End
+Class RecordList Extends List<Record>
+
+	Method Compare:Int(lhs:Record,rhs:Record)
+		If(lhs.score<rhs.score) Return -1
+		If(lhs.score>rhs.score) Return 1
+		If(lhs.mostCompletedSets<rhs.mostCompletedSets) Return -1
+		If(lhs.mostCompletedSets>rhs.mostCompletedSets) Return 1
+		If(lhs.mostBallSinRow<rhs.mostBallSinRow) Return -1
+		If(lhs.mostBallSinRow>rhs.mostBallSinRow) Return 1
+		If(lhs.mostPocketed<rhs.mostPocketed) Return -1
+		Return lhs.mostPocketed>rhs.mostPocketed
+	End
+End
+
+Class Record
+	Field name:String=""
+	Method SetName:Void(str:String)
+		Self.name=str
+	End
+	Field score:Int=0
+	Field mostBallSinRow:Int=0
+	Field mostPocketed:Int=0
+	Field mostCompletedSets:Int=0
+	Method GetCopy:Record()
+		Local record:Record=New Record()
+		record.name=Self.name
+		record.score=Self.score
+		record.mostBallSinRow=Self.mostBallSinRow
+		record.mostPocketed=Self.mostPocketed
+		record.mostCompletedSets=Self.mostCompletedSets
+		Return record
+	End
+	Method Render:Void(canvas:Canvas,x:Float,y:Float)
+		canvas.Color = New Color(1,1,1)
+		Text.Draw(canvas,name,x,y)
+		Text.Draw(canvas,String(score),x+160.0,y)
+		Text.Draw(canvas,String(mostCompletedSets),x+240.0,y)
+		Text.Draw(canvas,String(mostBallSinRow),x+330.0,y)
+		Text.Draw(canvas,String(mostPocketed),x+420.0,y)
+	End
+	Method IncrementPocketed:Void()
+		Self.mostPocketed+=1
+	End
+	Method Reset:Void()
+		Self.score=0
+		Self.mostBallSinRow=0
+		Self.mostPocketed=0
+		Self.mostCompletedSets=0
+	End
+	Method UpdateBallSinRow:Void(ballSinRow:Int)
+		If(ballSinRow>Self.mostBallSinRow)
+			Self.mostBallSinRow=ballSinRow
+		End
+	End
+	Method IncrementCompletedSets:Void()
+		Self.mostCompletedSets+=1
+	End
+End
+

+ 14 - 0
bananas/billiards/PoolMod/PoolMod.monkey2

@@ -0,0 +1,14 @@
+#Import  "<mojo>"
+#Import  "<std>"
+#Import  "Media"
+#Import  "Ball2D"
+#Import  "BallAnimation"
+#Import  "Common"
+#Import  "Engine"
+#Import  "FileSystem"
+#Import  "Font"
+#Import  "Gui"
+#Import  "PlayerRecords"
+
+Using mojo..
+Using std..

BIN
bananas/billiards/PoolMod/data/applaused.ogg


BIN
bananas/billiards/PoolMod/data/ball.png


BIN
bananas/billiards/PoolMod/data/bank01.ogg


BIN
bananas/billiards/PoolMod/data/bank02.ogg


BIN
bananas/billiards/PoolMod/data/beep.ogg


BIN
bananas/billiards/PoolMod/data/beep2.ogg


BIN
bananas/billiards/PoolMod/data/button.png


BIN
bananas/billiards/PoolMod/data/completion.ogg


BIN
bananas/billiards/PoolMod/data/fail.ogg


BIN
bananas/billiards/PoolMod/data/frame.png


BIN
bananas/billiards/PoolMod/data/frame3.png


BIN
bananas/billiards/PoolMod/data/hit03.ogg


BIN
bananas/billiards/PoolMod/data/hit04.ogg


BIN
bananas/billiards/PoolMod/data/hit05.ogg


BIN
bananas/billiards/PoolMod/data/hit06.ogg


BIN
bananas/billiards/PoolMod/data/pocket1.ogg


BIN
bananas/billiards/PoolMod/data/poolAtlasb.png


BIN
bananas/billiards/PoolMod/data/rackup.ogg


BIN
bananas/billiards/PoolMod/data/railhit.ogg


BIN
bananas/billiards/PoolMod/data/shoot.ogg


+ 1763 - 0
bananas/billiards/pool.monkey2

@@ -0,0 +1,1763 @@
+
+#Import "PoolMod/PoolMod"
+
+Function Main()
+	
+	New AppInstance
+	
+	New Game
+	
+	App.Run()
+End
+
+Class Game Extends Window
+
+	Field state:Int=0
+	Field fontImg:Image[]
+	Field frame1:FrameAnimation
+	Field frame2:FrameAnimation
+	Field pvcBtn:Button2
+	Field pvpBtn:Button2
+	Field soloBtn:Button2
+	Field scores:Button2
+	Field pool8:Pool
+	Field poolSolo:Pool
+	Field alpha:Float=.0
+	Field started:Int=0
+	Field down:Int=0
+	Field pool:Pool
+	
+	Method New(title:String="Simple mojo app",width:Int=640,height:Int=480,flags:WindowFlags=Null)
+		Super.New( title,width,height,flags )
+		state=1
+		atlasImg=Image.Load("asset::poolAtlasb.png")
+		If atlasImg = Null RuntimeError("Unable to load poolatlasb.png")
+		media=New Media(atlasImg)
+		LargeText=New VaryFont(atlasImg)
+		fontImg=LoadFrames(atlasImg,0,512,10,16,96)
+		If Not fontImg RuntimeError("Unable to load fontImg.png")
+		SetFont(fontImg,32)
+		Text=New BitmapText(fontImg,77)
+		frame1=New FrameAnimation(media,50,50,DEVICE_WIDTH-100,DEVICE_HEIGHT-100,2,40)
+		frame2=New FrameAnimation(media,50,20,DEVICE_WIDTH-100,DEVICE_HEIGHT-40,0,40)
+		Local buttonImg:Image[]=media.buttonImg
+		pvcBtn=New Button2(buttonImg,(DEVICE_WIDTH-buttonImg[0].Width)/2,120,"VS Computer",Null)
+		pvpBtn=New Button2(buttonImg,(DEVICE_WIDTH-buttonImg[0].Width)/2,200,"VS Player",Null)
+		soloBtn=New Button2(buttonImg,(DEVICE_WIDTH-buttonImg[0].Width)/2,280,"VS Time",Null)
+		scores=New Button2(buttonImg,(DEVICE_WIDTH-buttonImg[0].Width)/2,360,"Scores",Null)
+		inputName=New TextFieldGui(150,150,"Player 1",Null,200)
+		pool8= New EightBall()
+		poolSolo= New PoolSoloTime()
+		scoreTableVsTime= New ScoreTable("scoreTable.dat")
+		alpha=0.0
+		started=1
+		frame1.Init()
+	End
+	
+	Method OnUpdate:Int()
+		
+		If Mouse.ButtonDown(MouseButton.Left) Self.down=1
+		If state=1
+			If(Mouse.ButtonDown(MouseButton.Left)=0 And down=1)
+				frame2.Init()
+				alpha=0
+				down=0
+				state=2
+			End
+		ElseIf state=2
+			pvcBtn.Update()
+			pvpBtn.Update()
+			soloBtn.Update()
+			scores.Update()
+			If True = pvcBtn.GetState()
+				pool=pool8
+				pool.SetupPVC()
+				state=6
+				pool.Reset()
+			Elseif True = pvpBtn.GetState()
+				pool = pool8
+				pool.SetupPVP()
+				state = 6
+				pool.Reset()
+			Elseif True = soloBtn.GetState()
+				pool = poolSolo
+				pool.SetupTimed()
+				state=6
+				pool.InitGame()
+			Elseif True = scores.GetState()
+				state=7
+				scoreTableVsTime.InitFrame()
+			End
+		ElseIf(state=6)
+			If pool.Update()=0
+				Self.state=7
+				Local record:Record=Self.pool.GetRecord()
+				scoreTableVsTime.SetRecord(record)
+				scoreTableVsTime.SaveTable()
+				scoreTableVsTime.InitFrame()
+			End
+		ElseIf state=7
+			If scoreTableVsTime.Update()=False
+				Self.frame2.Init()
+				Self.alpha=0
+				Self.down=0
+				Self.state=2
+			End
+		Elseif(state=4)
+		End
+		Return 1
+	End
+	
+	Method OnRender(canvas:Canvas) Override
+
+		OnUpdate()
+
+		App.RequestRender()
+
+		canvas.Color = New Color(1,1,1)
+		canvas.DrawImage(media.titleImg,0.0,0.0)
+		If(state=1)
+			Self.frame1.Render(canvas)
+			If(started=1)
+				canvas.Color = New Color(1,1,1)
+				canvas.DrawImage(media.titleImg,0.0,0.0)
+				Self.started=0
+			Else
+				If Self.frame1.Update()=0
+					canvas.DrawImage(media.logoImg,180.0,60.0)
+					Local str:String="HEXOBOT"
+					Local x:Int=(DEVICE_WIDTH-LargeText.TextWidth(str))/2
+					Local y:Int=340
+					If(Self.alpha<1.0)
+						 canvas.Alpha = alpha
+						Self.alpha+=.02
+					End
+					str="Touch To Continue"
+					canvas.Color = New Color(1,1,1)
+					x= (DEVICE_WIDTH-Text.Width(str))/2
+					y=DEVICE_HEIGHT-80
+					Text.Draw(canvas,str,x,y)
+				End
+			End
+			canvas.Alpha = 1.0
+		ElseIf(state=2)
+			Self.frame2.Render(canvas)
+			If(Self.frame2.Update()=0)
+				canvas.DrawImage(media.logoImg,180.0,60.0,0)
+				If(Self.alpha<1.0)
+					canvas.Alpha = alpha
+					alpha+=0.02
+				End
+				pvcBtn.Render(canvas)
+				pvpBtn.Render(canvas)
+				soloBtn.Render(canvas)
+				scores.Render(canvas)
+			End
+			canvas.Alpha = 1.0
+		Elseif(state=6)
+			Self.pool.Render(canvas)
+		Elseif(state=7)
+			scoreTableVsTime.Render(canvas)
+		End
+	End
+End
+
+
+Global atlasImg:Image
+
+Global inputName:TextFieldGui
+
+Class Pool
+	Field engine:ElasticEngine
+	Field ballList:List<Ball>
+	Field arcList:List<ArcWall>
+	Field pottedList:List<Ball>
+	Field turnList:List<Ball>
+	Field tableImg:Image
+	Field arrowsImg:Image
+	Field ballImg:Image
+	Field railAnimation:RailAnimation
+	Field Exiting:Int=0
+	Field player:Record
+	Field state:Int=0
+	Field cue:Ball
+	
+	Field activePlayer:User
+	Field otherPlayer:User
+	Field incrementer:Int=0
+	Field scoreLevel:Int=1
+	Field maxLevel:Int=15
+	Field decrementer:Int=0
+	
+	Field user:User
+	Field minLevel:Int
+	
+	Method Setup16()
+		For Local a:ArcWall = Eachin arcList
+			Select a.num
+				Case 1 a.SetTarget(a.P.x+16.0,a.P.y+16.0)
+				Case 2 a.SetTarget(a.P.x,a.P.y+15.0)
+				Case 3 a.SetTarget(a.P.x-16.0,a.P.y+16.0)
+				Case 4 a.SetTarget(a.P.x-16.0,a.P.y-16.0)
+				Case 5 a.SetTarget(a.P.x,a.P.y-15.0)
+				Case 6 a.SetTarget(a.P.x+16.0,a.P.y-16.0)
+			End Select
+		Next
+		Self.engine.Add3DBall(180.8,180.0,10.0,0.0,0.0,media.ballImg,new Color(1,1,.1),new Color(.6,.2,.6),1,0)
+		Self.engine.Add3DBall(162.6,170.0,10.0,0.0,0.0,media.ballImg,new Color(.3,.3,1),new Color(1,1,0),2,0)
+		Self.engine.Add3DBall(162.6,190.0,10.0,0.0,0.0,media.ballImg,new Color(1,.25,.25),new Color(1,1,0),3,0)
+		Self.engine.Add3DBall(144.4,160.0,10.0,0.0,0.0,media.ballImg,new Color(.35,.2,1),new Color(1,1,0),4,0)
+		Self.engine.Add3DBall(144.4,200.0,10.0,0.0,0.0,media.ballImg,new Color(1,.35,.12),new Color(0,1,1),5,0)
+		Self.engine.Add3DBall(126.2,150.0,10.0,0.0,0.0,media.ballImg,new Color(.12,.8,.12),New Color(0,0,1),6,0)
+		Self.engine.Add3DBall(126.2,170.0,10.0,0.0,0.0,media.ballImg,new Color(.8,.1,.1),new Color(.9,.9,.9),7,0)
+		Self.engine.Add3DBall(144.4,180.0,10.0,0.0,0.0,media.ballImg,new Color(.35,.35,.35),new Color(.93,.93,.93),8,0)
+		Self.engine.Add3DBall(126.2,190.0,10.0,0.0,0.0,media.ballImg,new Color(1,1,.1),new Color(.6,.2,.6),9,0)
+		Self.engine.Add3DBall(126.2,210.0,10.0,0.0,0.0,media.ballImg,new Color(.3,.3,1),new Color(1,1,0),10,0)
+		Self.engine.Add3DBall(108.0,140.0,10.0,0.0,0.0,media.ballImg,new Color(1,.25,.25),new Color(1,1,0),11,0)
+		Self.engine.Add3DBall(108.0,160.0,10.0,0.0,0.0,media.ballImg,new Color(.35,.2,1),new Color(1,1,0),12,0)
+		Self.engine.Add3DBall(108.0,180.0,10.0,0.0,0.0,media.ballImg,new Color(1,.35,.12),new Color(0,1,1),13,0)
+		Self.engine.Add3DBall(108.0,200.0,10.0,0.0,0.0,media.ballImg,new Color(.12,.8,.12),New Color(0,0,1),14,0)
+		Self.engine.Add3DBall(108.0,220.0,10.0,0.0,0.0,media.ballImg,new Color(.8,.1,.1),new Color(.9,.9,.9),15,0)
+		Self.cue=Self.engine.Add3DBall(490.0,180.0,10.0,-15.0,0.05,media.ballImg,new Color(1,1,1),new Color(.6,.2,.6),16,0)
+	End
+	Method SetupPVC() Abstract
+	Method Reset() Abstract
+	Method SetupPVP() Abstract
+	Method SetupTimed() Abstract
+	Method InitGame() Abstract
+	Method Update:Int() Abstract
+	
+	Method GetRecord:Record()
+		Return Self.player
+	End
+	
+	Method Render(canvas:Canvas) Abstract
+
+	Method BallInHole()
+		For Local a:ArcWall = Eachin arcList
+			For Local b:Ball = Eachin ballList
+				If(a.BallInside(b))
+					engine.RemoveBall(b)
+					pottedList.AddLast(b)
+					turnList.AddLast(b)
+					railAnimation.AddJob(b)
+					media.PlayPocketCol()
+					If(b.num<16)
+						incrementer+=5000
+						player.score+=scoreLevel*100
+						scoreLevel+=1
+						If scoreLevel>maxLevel scoreLevel=maxLevel
+						player.IncrementPocketed()
+					Else
+						decrementer += 30000
+						media.PlayScratch()
+					End
+				End
+			End
+		End
+	End
+	Method ListContains:Bool(list:List<Ball>,num:Int)
+		For Local b2:Ball = Eachin list
+			If(b2.num=num)
+				Return True
+			End
+		End
+		Return False
+	End
+
+	Method ListContains:Bool(list:List<Ball>,b:Ball)
+		For Local b2:Ball = Eachin list
+			If(b2=b) Return True
+		Next
+		Return False
+	End
+	
+	Method RestoreCue() Virtual	
+		state=9
+		cue.Init(0,0)
+		cue.SetPosition(490,180)
+		turnList.RemoveEach(cue)
+		pottedList.RemoveEach(cue)
+		railAnimation.RemoveCueBall()
+		engine.AddBall(cue)
+	End
+	
+	Method ListContainsSolids:Bool(list:List<Ball>)
+		For Local b:Ball = Eachin list
+			If(b.num<8) Return True
+		End
+		Return False
+	End
+	
+	Method ListContainsStripes:Bool(list:List<Ball>)
+		For Local b:Ball = Eachin list
+			If(b.num>8 And b.num<16) Return True
+		End
+		Return False
+	End
+	
+End
+
+Class EightBall Extends Pool
+	Field inacuracy:Float
+	Field timeFrame:Float
+	Field lastTime:Float
+	Field down:Int=0
+
+	Field finalText1:String=""
+	Field finalText2:String=""
+	
+	Method Init()
+		Self.Exiting=1
+	End
+	
+	Method New()
+		inacuracy=0.4
+		engine=New ElasticEngine()
+		engine.SetGravity(0.0,0.0)
+		engine.SetFriction(0.98)
+		ballList=Self.engine.ballList
+		arcList=New List<ArcWall>
+		pottedList= New List<Ball>
+		turnList=New List<Ball>
+		tableStorage= New TableStorage()
+		tableStorage.GetTable(0,Self.engine,Self.arcList)
+		tableImg=media.tableImg
+		arrowsImg=media.arrowsImg
+		ballImg=media.ballImg
+		railAnimation=New RailAnimation()
+		railAnimation.ShowStops(0)
+		Init()
+		player=New Record()
+		state=1
+		Setup16()
+		lastTime = Millisecs()
+	End
+
+	Method SetupTimed() Override
+		RuntimeError("invald attemp to create 8ball pool timed")
+	End
+	
+	Method SetupPVC() Override
+		Self.activePlayer= New Player("Player 1",cue,engine,media.ghostImg,media.ballImg,media.stickImg,media.rotatorImg,media.buttonImg,media.stripImg,media.frameTopImg,media.frameBotImg,media.topImg,media.botImg,media.lrImg,media.meterImg,media.stretchImg,media.sliderImg)
+		Self.otherPlayer= New Cpu("Cpu",cue,engine,arcList,media.stickImg,media.ballImg,inacuracy)
+	End
+	
+	Method SetupPVP() Override
+		Self.activePlayer=New Player("Player 1",cue,engine,media.ghostImg,media.ballImg,media.stickImg,media.rotatorImg,media.buttonImg,media.stripImg,media.frameTopImg,media.frameBotImg,media.topImg,media.botImg,media.lrImg,media.meterImg,media.stretchImg,media.sliderImg)
+		Self.otherPlayer=New Player("Player 2",cue,engine,media.ghostImg,media.ballImg,media.stickImg,media.rotatorImg,media.buttonImg,media.stripImg,media.frameTopImg,media.frameBotImg,media.topImg,media.botImg,media.lrImg,media.meterImg,media.stretchImg,media.sliderImg)
+	End
+	
+	Method InitGame() Override
+	End
+	
+	Method Reset() Override
+		For Local b:Ball= Eachin Self.pottedList
+			Self.engine.AddBall(b)
+			Self.pottedList.RemoveEach(b)
+		End
+		Self.turnList.Clear()
+		Self.railAnimation.Reset()
+		For Local b2:Ball = Eachin Self.ballList
+			b2.Init(0.0,0.0)
+			If(b2.num=1)
+				b2.SetPosition(180.8,180.0)
+			ElseIf(b2.num=2)
+				b2.SetPosition(162.6,170.0)
+			ElseIf(b2.num=3)
+				b2.SetPosition(162.6,190.0)
+			ElseIf(b2.num=4)
+				b2.SetPosition(144.4,160.0)
+			ElseIf(b2.num=5)
+				b2.SetPosition(144.4,200.0)
+			ElseIf(b2.num=6)
+				b2.SetPosition(126.2,150.0)
+			ElseIf(b2.num=7)
+				b2.SetPosition(126.2,170.0)
+			ElseIf(b2.num=8)
+				b2.SetPosition(144.4,180.0)
+			ElseIf(b2.num=9)
+				b2.SetPosition(126.2,190.0)
+			ElseIf(b2.num=10)
+				b2.SetPosition(126.2,210.0)
+			ElseIf(b2.num=11)
+				b2.SetPosition(108.0,140.0)
+			ElseIf(b2.num=12)
+				b2.SetPosition(108.0,160.0)
+			ElseIf(b2.num=13)
+				b2.SetPosition(108.0,180.0)
+			ElseIf(b2.num=14)
+				b2.SetPosition(108.0,200.0)
+			ElseIf(b2.num=15)
+				b2.SetPosition(108.0,220.0)
+			ElseIf(b2.num=16)
+				b2.SetPosition(490.0,180.0)
+			End
+			b2.ResetAnimation()
+		Next
+		
+		Self.state=2
+		Self.Exiting=1
+		Self.down=False
+	
+	End
+	
+	Method AssignBalls()
+		For Local b:Ball = Eachin Self.turnList
+			If(Self.activePlayer.OwnsSolids())
+				If(b.num<=8)
+					Self.activePlayer.AddBall(b)
+				Else
+					If(b.num>8)
+						Self.otherPlayer.AddBall(b)
+					End
+				End
+			Else
+				If(Self.activePlayer.OwnsStripes())
+					If(b.num>=8)
+						Self.activePlayer.AddBall(b)
+					Else
+						Self.otherPlayer.AddBall(b)
+					End
+				Else
+					If(b.num<8)
+						Self.activePlayer.SetToSolids()
+						Self.otherPlayer.SetToStripes()
+						Self.activePlayer.AddBall(b)
+					Else
+						If(b.num>8)
+							Self.activePlayer.SetToStripes()
+							Self.otherPlayer.SetToSolids()
+							Self.activePlayer.AddBall(b)
+						Else
+							Self.activePlayer.AddBall(b)
+						End
+					End
+				End
+			End
+		End
+	End
+	
+	Method ResolveTurn()
+		If Self.turnList.Empty
+			Self.Exiting=0
+			Self.state=10
+			Return
+		End
+		Self.state=3
+		If(Self.Exiting=1)
+			For Local b:Ball = Eachin turnList
+				If(b.num<8)
+					activePlayer.SetToSolids()
+					otherPlayer.SetToStripes()
+					Exit
+				End
+				If(b.num>8 And b.num<16)
+					activePlayer.SetToStripes()
+					otherPlayer.SetToSolids()
+					Exit
+				End
+			End
+			If ListContains(turnList,cue)
+				state=9
+				If ListContains(turnList,8)
+					state=7
+				Else
+					RestoreCue()
+				End
+			Else
+				If(ListContains(Self.turnList,8))
+					state=6
+				End
+			End
+			For Local b2:Ball = Eachin turnList
+				If(activePlayer.OwnsSolids())
+					If b2.num<=8
+						activePlayer.AddBall(b2)
+					Elseif b2.num>8 And b2.num<16
+						otherPlayer.AddBall(b2)
+					Else
+						RestoreCue()
+					End
+				Else
+					If activePlayer.OwnsStripes()
+						If b2.num>=8 And b2.num<16
+							activePlayer.AddBall(b2)
+						Elseif b2.num<8
+							otherPlayer.AddBall(b2)
+						Else
+							RestoreCue()
+						End
+					ElseIf b2.num=8
+						activePlayer.AddBall(b2)
+					End
+				End
+			End
+			Self.Exiting=0
+		Else
+			Local b3:Ball=engine.GetFirstCollision()
+			If activePlayer.CompletedSet()=True
+				If ListContains(turnList,8)
+					state=6
+					If ListContains(turnList,cue) state=7
+					If activePlayer.OwnsSolids()
+						If b3.num>8 And b3.num<16 state=7
+					Else
+						If b3.num<8 Self.state=7
+					End
+				Else
+					If ListContains(turnList,cue)
+						state=7
+					Else
+						state=10
+					End
+				End
+			Else
+				If b3<>Null
+					If b3.num<8 And activePlayer.OwnsStripes() state=10
+					If b3.num>8 And b3.num<16 And activePlayer.OwnsSolids() state=10
+				End
+				If activePlayer.OwnsSolids()
+					If ListContainsSolids(turnList)=False state=10
+				Else
+					If activePlayer.OwnsStripes()
+						If ListContainsStripes(turnList)=False state=10
+					End
+				End
+				If ListContains(turnList,cue) RestoreCue()
+				If ListContains(turnList,8) state=7
+				Self.AssignBalls()
+			End
+		End
+		Self.turnList.Clear()
+		Return
+	End
+	
+	Method SwitchPlayers()
+		Self.user=activePlayer
+		Self.activePlayer=otherPlayer
+		Self.otherPlayer=user
+	End
+	
+	Method Update:Int() Override
+		Local SelfTime:Float = Millisecs()
+		timeFrame=(SelfTime-lastTime)/30.0
+		lastTime=SelfTime
+		Local done:Int=0
+		If state=1
+			state=2
+			activePlayer.SetButtonText("position")
+			activePlayer.Init()
+		ElseIf state=2
+			If Not activePlayer.Positioning()
+				state=3
+				activePlayer.SetButtonText("Shoot")
+				activePlayer.Init()
+			End
+		ElseIf state=3
+			If Not activePlayer.Aiming()
+				state=4
+			End
+		ElseIf state=4
+			If Not activePlayer.ShootingAnimating()
+				lastTime=(Millisecs())
+				state=5
+			End
+		ElseIf state=5
+			engine.Update(timeFrame)
+			BallInHole()
+			If Not engine.BallsMoving()
+				ResolveTurn()
+				engine.ClearFirstCollision()
+				activePlayer.Init()
+			End
+		ElseIf state=10 
+			SwitchPlayers()
+			activePlayer.Init()
+			state=3
+		ElseIf state=9
+			SwitchPlayers()
+			activePlayer.SetButtonText("position")
+			activePlayer.Init()
+			state=2
+		ElseIf state=6
+			finalText1=activePlayer.name+" WINS GAME"
+			finalText2=otherPlayer.name+" LOST GAME"
+			state=8
+		ElseIf state=7
+			finalText1=otherPlayer.name+" WINS GAME"
+			finalText2=activePlayer.name+" LOST GAME"
+			state=8
+		ElseIf state=8
+			If Mouse.ButtonDown(MouseButton.Left) down=1
+			If Not Mouse.ButtonDown(MouseButton.Left) And down=1 done=1
+		Endif 
+		railAnimation.Update(timeFrame*2.0)
+		Return Not done	
+	End Method 
+	
+	Method Render(canvas:Canvas) Override
+		canvas.DrawImage(media.railImg,5.0,360.0,0)
+		Self.railAnimation.Render(canvas)
+		canvas.Color = New Color(1,1,1)
+		canvas.DrawImage(Self.tableImg,0.0,0.0,0)
+		Self.engine.Render(canvas)
+		canvas.Color = New Color(1,1,1)
+		If(state=2)
+			canvas.Alpha = .2
+			canvas.DrawRect(466.0,37.0,140.0,288.0)
+			canvas.Alpha = .3
+			canvas.DrawImage(arrowsImg,cue.P.x,cue.P.y,0)
+			canvas.Alpha = 1.0
+			activePlayer.RenderButton(canvas)
+		Elseif state=4 Or state=3
+			activePlayer.Render(canvas,1)
+		ElseIf state=8
+			Text.Draw(canvas,finalText1,200,100)
+			Text.Draw(canvas,finalText2,200,150)
+		End
+		Local x:Float=25.0
+		canvas.Color = New Color(1,1,1)
+	End
+	
+End
+
+Class TableStorage
+	Field currentTable:Int=0
+	Method New()
+		Self.currentTable=-1
+	End
+	Field table:Table
+	
+	Method GetTable:Table(index:Int,engine:ElasticEngine,arcList:List<ArcWall>)
+		If currentTable=index
+			Return Self.table
+		Else
+			If(index<1 And index>-1)
+				If index = 0
+					engine.RemoveWalls()
+					Local wall:Wall[]=New Wall[](New Arc(28.0,28.0,17.0,135.0,315.0,0.0,0.0,media.arcImg,New Color(1,0,0)),
+					New Line(41.0,17.0,58.0,34.0,media.lineImg,new Color(1,0,0)),
+					New Line(59.0,34.0,296.0,34.0,media.lineImg,new Color(1,0,0)),
+					New Line(296.0,33.0,303.0,25.0,media.lineImg,new Color(1,0,0)),
+					New Line(303.0,24.0,303.0,19.0,media.lineImg,new Color(1,0,0)),
+					New Arc(320.0,18.0,17.0,180.0,360.0,0.0,0.0,media.arcImg,new Color(1,0,0)),
+					New Line(338.0,19.0,338.0,24.0,media.lineImg,new Color(1,0,0)),
+					New Line(337.0,25.0,345.0,33.0,media.lineImg,new Color(1,0,0)),
+					New Line(345.0,34.0,582.0,34.0,media.lineImg,new Color(1,0,0)),
+					New Line(583.0,33.0,599.0,17.0,media.lineImg,new Color(1,0,0)),
+					New Arc(612.0,28.0,17.0,225.0,405.0,0.0,0.0,media.arcImg,new Color(1,0,0)),
+					New Line(624.0,41.0,609.0,57.0,media.lineImg,new Color(1,0,0)),
+					New Line(608.0,57.0,608.0,304.0,media.lineImg,new Color(1,0,0)),
+					New Line(609.0,305.0,624.0,320.0,media.lineImg,new Color(1,0,0)),
+					New Arc(613.0,333.0,17.0,-45.0,135.0,0.0,0.0,media.arcImg,new Color(1,0,0)),
+					New Line(599.0,344.0,584.0,328.0,media.lineImg,new Color(1,0,0)),
+					New Line(584.0,327.0,345.0,327.0,media.lineImg,new Color(1,0,0)),
+					New Line(344.0,328.0,338.0,335.0,media.lineImg,new Color(1,0,0)),
+					New Line(338.0,336.0,338.0,341.0,media.lineImg,new Color(1,0,0)),
+					New Arc(320.0,342.0,17.0,0.0,180.0,0.0,0.0,media.arcImg,new Color(1,0,0)),
+					New Line(303.0,341.0,303.0,336.0,media.lineImg,new Color(1,0,0)),
+					New Line(303.0,335.0,296.0,328.0,media.lineImg,new Color(1,0,0)),
+					New Line(295.0,327.0,59.0,327.0,media.lineImg,new Color(1,0,0)),
+					New Line(58.0,328.0,41.0,344.0,media.lineImg,new Color(1,0,0)),
+					New Arc(27.0,333.0,17.0,45.0,225.0,0.0,0.0,media.arcImg,new Color(1,0,0)),
+					New Line(16.0,320.0,31.0,305.0,media.lineImg,new Color(1,0,0)),
+					New Line(33.0,304.0,33.0,58.0,media.lineImg,new Color(1,0,0)),
+					New Line(32.0,57.0,17.0,43.0,media.lineImg,new Color(1,0,0)))
+					table=New Table(wall,engine,arcList)
+					Return table
+				End
+			End
+		End
+		Return Null
+	End
+End
+
+Global tableStorage:TableStorage
+
+Class Table
+
+	Method New(wall:Wall[],engine:ElasticEngine,arcList:List<ArcWall>)
+		Local n:Int=1, t:Int=0
+		While(t < wall.Length)
+			Local w:Wall=wall[t]
+			t=t+1
+			If w.name ="Line"
+				Local l:Line=Cast<Line>(w)
+				If l.image
+					engine.AddLineWallImage(l.x1,l.y1,l.x2,l.y2,w.image,New Color(0,0,0))
+				Else
+					engine.AddLineWallOutline(l.x1,l.y1,l.x2,l.y2,Null,l.color)
+				Endif
+			ElseIf w.name ="Arc"
+				Local a:Arc=Cast<Arc>(w)
+				If a.image
+					Local w:ArcWall=engine.AddArcWallImage(a.x1,a.y1,a.radius,a.startAngle,a.endAngle,a.image,New Color(1,1,1))
+					arcList.AddLast(w)
+					w.num=n
+					n+=1
+				Else
+					Local w:ArcWall=engine.AddArcWallOutline(a.x1,a.y1,a.radius,a.startAngle,a.endAngle,a.color)
+					If arcList = Null Then RuntimeError("arclist not initialized")
+					arcList.AddLast(w)
+					w.num=n
+					n+=1
+				Endif
+			ElseIf(w.name ="Circle")
+
+			Endif
+		End
+	End
+End
+
+Class PoolSoloTime Extends Pool
+	
+	Field blinkOn:Int=0
+	Field timed:Int
+	Field minutes:Int
+	Field seconds:Int
+	Field lastTime:Float
+	Field timeFrame:Float
+	Field blinkTime:Int
+	Field blink:Int
+	Field oldTime:Int
+	
+	Field endTime:Int=0
+	Field time:Int=0
+		
+	Field down:Int=0
+
+	Method New()
+		Self.engine=New ElasticEngine()
+		Self.engine.SetGravity(0.0,0.0)
+'		Self.engine.SetFriction(0.970)
+		Self.ballList=Self.engine.ballList
+		Self.arcList=New List<ArcWall>
+		Self.pottedList= New List<Ball>
+		Self.turnList= New List<Ball>
+		tableStorage= New TableStorage()
+		tableStorage.GetTable(0,Self.engine,Self.arcList)
+		Self.tableImg=media.tableImg
+		Self.arrowsImg=media.arrowsImg
+		Self.ballImg=media.ballImg
+		Self.railAnimation=New RailAnimation()
+		Self.railAnimation.ShowStops(0)
+		Self.Exiting=1
+		Self.Setup16()
+		Self.player=New Record()
+		Self.player.SetName("Player")
+		Self.blinkOn=0
+	End
+	
+	Method SetupPVC() Override
+		RuntimeError("invalid Setup in Solo Play")
+	End
+	Method SetupPVP() Override
+		RuntimeError("invalid Setup in Solo Play")
+	End
+	
+	Method InitTime()
+		Self.endTime=Millisecs()+180000
+		Self.time=180000
+	End
+	
+	Method SetupTimed() Override
+		Self.activePlayer= New Player("",cue,engine,media.ghostImg,  media.ballImg,media.stickImg,media. rotatorImg,media.buttonImg,media.stripImg,  media.frameTopImg,media.  frameBotImg,media.  topImg,media.  botImg,  media.lrImg,  media.meterImg,media.stretchImg,media.sliderImg)
+		Self.InitTime()
+	End
+	
+	Method Reset() Override
+		For Local b:Ball = Eachin Self.pottedList
+			Self.engine.AddBall(b)
+			Self.pottedList.RemoveEach(b)
+		End
+		
+		Self.turnList.Clear()
+		Self.railAnimation.Reset()
+		
+		For Local b2:Ball = Eachin Self.ballList
+			b2.Init(0.0,0.0)
+			If b2.num=1
+				b2.SetPosition(180.8,180.0)
+			Elseif(b2.num=2)
+				b2.SetPosition(162.6,170.0)
+			Elseif(b2.num=3)
+				b2.SetPosition(162.6,190.0)
+			Elseif(b2.num=4)
+				b2.SetPosition(144.4,160.0)
+			Elseif(b2.num=5)
+				b2.SetPosition(144.4,200.0)
+			Elseif(b2.num=6)
+				b2.SetPosition(126.2,150.0)
+			Elseif(b2.num=7)
+				b2.SetPosition(126.2,170.0)
+			Elseif(b2.num=8)
+				b2.SetPosition(144.4,180.0)
+			Elseif(b2.num=9)
+				b2.SetPosition(126.2,190.0)
+			Elseif(b2.num=10)
+				b2.SetPosition(126.2,210.0)
+			Elseif(b2.num=11)
+				b2.SetPosition(108.0,140.0)
+			Elseif(b2.num=12)
+				b2.SetPosition(108.0,160.0)
+			Elseif(b2.num=13)
+				b2.SetPosition(108.0,180.0)
+			Elseif(b2.num=14)
+				b2.SetPosition(108.0,200.0)
+			Elseif(b2.num=15)
+				b2.SetPosition(108.0,220.0)
+			End
+			b2.ResetAnimation()
+		End
+		Exiting=1
+	End
+	
+	Method InitGame() Override
+		player.Reset()
+		activePlayer.ResetBalls()
+		Reset()
+		Exiting=1
+		state=12
+		cue.SetPosition(490.0,180.0)
+		InitTime()
+	End
+	
+	Method RestoreCue() Override
+		state=9
+		cue.Init(0.0,0.0)
+		cue.SetPosition(320.0,180.0)
+		turnList.RemoveEach(cue)
+		pottedList.RemoveEach(cue)
+		railAnimation.RemoveCueBall()
+		engine.AddBall(cue)
+	End
+	
+	Method ResolveTurn()
+		Self.state=3
+		If Not turnList.Empty
+			For Local b:Ball = Eachin Self.turnList
+				If(b.num<16)
+					activePlayer.AddBall(b)
+				Else
+					RestoreCue()
+					player.UpdateBallSinRow(Self.scoreLevel)
+					scoreLevel=Self.minLevel
+				End
+			End
+			Self.turnList.Clear()
+		Else
+			Self.scoreLevel=Self.minLevel
+		End
+		If(Self.activePlayer.CompletedAll()=True)
+			player.IncrementCompletedSets()
+			activePlayer.ResetBalls()
+			Reset()
+		End
+		Self.Exiting=0
+	End
+
+	Method Update:Int() Override
+		If(decrementer>0)
+			decrementer-=500
+			endTime-=500
+		End
+		If incrementer>0
+			incrementer-=200
+			endTime+=500
+			timed+=500
+			If(timed>=2000)
+				media.PlayBeep()
+				timed=0
+			End
+		End
+		time= (endTime-Millisecs())/1000
+		minutes= time/60
+		seconds=time Mod 60
+		Local SelfTime:Float= Millisecs()
+		timeFrame=(SelfTime-lastTime)/30.0
+		lastTime=SelfTime
+		If time<=0
+			player.UpdateBallSinRow(Self.scoreLevel)
+			state=6
+			blinkOn=0
+		Else
+			If time<=10
+				blinkOn=1
+				If Millisecs()>= blinkTime+300
+					blink = Not (blink<>0)
+					blinkTime = Millisecs()
+				End
+				If time<oldTime media.PlayBeepB()
+				oldTime = time
+			Else
+				oldTime=time
+				blinkTime=Millisecs()
+				blinkOn=0
+			End
+		End
+		Local done:Int=0
+		If state=12
+			state = 1
+			Local t_7:Int=inputName.Update()
+			If(t_7=-1)
+				Return 0
+			Else
+				If(t_7=0)
+					player.SetName(inputName.GetText())
+					state=1
+				End
+			End
+		Elseif state=1
+			state=2
+			activePlayer.SetButtonText("position")
+			activePlayer.Init()
+		Elseif state=2
+			If activePlayer.Positioning()=False
+				state=3
+				activePlayer.SetButtonText("Shoot")
+				activePlayer.Init()
+				InitTime()
+			End
+		Elseif state=3
+			If activePlayer.Aiming()=False state=4
+		Elseif state=4
+			If activePlayer.ShootingAnimating()=False state=5
+		Elseif state=5
+			engine.Update(timeFrame)
+			BallInHole()
+			If engine.BallsMoving()=False
+				ResolveTurn()
+				engine.ClearFirstCollision()
+				activePlayer.Init()
+			End
+		Elseif state=9
+			activePlayer.SetButtonText("Shoot")
+			activePlayer.Init()
+			state=3
+		Elseif state=6
+			If Mouse.ButtonDown(MouseButton.Left)<>0 down=1
+			If Mouse.ButtonDown(MouseButton.Left)=0 And down=1
+				down=0
+				done=1
+			End
+		Elseif state=8
+		Endif
+		Self.railAnimation.Update(timeFrame*3.0)
+		Return Not done
+	End
+	
+	Method Render(canvas:Canvas) Override	
+		canvas.DrawImage(media.railImg,5.0,360.0,0)
+		railAnimation.Render(canvas)
+		canvas.Color = New Color(1,1,1)
+		canvas.DrawImage(tableImg,0.0,0.0,0)
+		engine.Render(canvas)
+		If(state=12)
+			canvas.Color = New Color(1,1,1)
+			inputName.Render(canvas)
+		Elseif(state=2)
+			canvas.Alpha = .2
+			canvas.DrawRect(466.0,37.0,140.0,288.0)
+			canvas.Alpha = .3
+			canvas.DrawImage(arrowsImg,cue.P.x,cue.P.y,0)
+			canvas.Alpha = 1.0
+			activePlayer.RenderButton(canvas)
+		ElseIf(state=4 Or state=3)
+			If decrementer>0
+				canvas.Color = New Color(1,.4,0)
+			Else
+				canvas.Color = New Color(1,1,1)
+			End
+			Local sec:String=String(seconds)
+			If(sec.Length = 1) sec="0"+sec
+			If blinkOn
+				If blink Text.Draw(canvas,"Time Left       "+String(minutes)+":"+sec,400,370)
+			Else
+				Text.Draw(canvas,"Time Left       "+String(minutes)+":"+sec,400,370)
+			End
+			canvas.Color = New Color(1,1,1)
+			Text.Draw(canvas,"Score           "+String(player.score),400,400)
+			Text.Draw(canvas,"Multiplier 100 x"+String(scoreLevel),400,430)
+			activePlayer.Render(canvas,0)
+		ElseIf(state=5)
+			If decrementer > 0
+				canvas.Color = New Color(1,.4,0)
+			Else
+				canvas.Color = New Color(1,1,1)
+			End
+			Local sec2:String=String(seconds)
+			If sec2.Length=1  sec2="0"+sec2
+			If blinkOn<>0
+				If blink Text.Draw(canvas,"Time Left       "+String(minutes)+":"+sec2,400,370)
+			Else
+				Text.Draw(canvas,"Time Left       "+String(minutes)+":"+sec2,400,370)
+			End
+			canvas.Color = New Color(1,1,1)
+			Text.Draw(canvas,"Score           "+String(player.score),400,400)
+			Text.Draw(canvas,"Multiplier 100 x"+String(scoreLevel),400,430)		
+		ElseIf(state=6)
+			Local tx:Int=120
+			Local ty:Int=100
+			canvas.Alpha = .7
+			canvas.Color = New Color(.2,0,1)
+			canvas.DrawRect(tx-20,ty-20,400.0,240.0)
+			canvas.Alpha = 1.0
+			canvas.Color = New Color(1,1,1)
+			canvas.DrawText("Name                       "+player.name,tx,ty+0,0.0,0.0)
+			canvas.DrawText("Score                      "+String(player.score),tx,ty+30,0.0,0.0)
+			canvas.DrawText("completed Sets             "+String(player.mostCompletedSets),tx,ty+60,0.0,0.0)
+			canvas.DrawText("Most Balls In A Row        "+String(player.mostBallSinRow),tx,ty+90,0.0,0.0)
+			canvas.DrawText("Most Pocketed              "+String(player.mostPocketed),tx,ty+120,0.0,0.0)
+			canvas.DrawText(" Press to exit             ",tx+100,ty+170,0.0,0.0)
+		ElseIf(state=8)
+
+		End
+	End
+End
+
+
+Global scoreTableVsTime:ScoreTable
+
+Class User
+	Field ballList:List<Ball>
+
+	Field cue:Ball
+	Field position:PVector2D
+	Field ballImg:Image
+	Field ghostImg:Image[]
+	Field cueStick:CueStick
+	Field name:String=""
+	Global firstShot:Int
+	Field completedSet:Int=0
+	Field engine:ElasticEngine
+	Field ghostBall:GhostBall
+	Field control:Control
+	Field button:Button2
+	Field ballOwnerType:Int=0
+	Field angle:Float
+	Field oldAngle:Float
+	
+	Method New()
+		ballList=New List<Ball>
+	End
+	
+	Method SetButtonText(str:String) Virtual
+		Return
+	End
+	
+	Method Init() Abstract
+	Method Positioning:Bool() Abstract
+	Method Aiming:Bool() Abstract
+
+	Method ShootingAnimating:Bool() Abstract
+
+	Method SetToSolids()
+		ballOwnerType=2
+	End
+
+	Method SetToStripes()
+		ballOwnerType=1
+	End
+	Method OwnsSolids:Bool()
+		Return ballOwnerType=2
+	End
+	
+	Method AddBall(b:Ball)
+		Self.ballList.AddLast(b)
+	End
+
+	Method OwnsStripes:Bool()
+		Return Self.ballOwnerType=1
+	End
+	
+	Method CompletedSet:Bool()
+		Return Self.ballList.Count()=7
+	End
+	
+	Method RenderButton(canvas:Canvas) Abstract
+
+	Method Render(canvas:Canvas,state:Int) Virtual
+		canvas.Color = New Color(0.0,0.0,0.0)
+		Text.Draw(canvas, name,544,371)
+		canvas.Color = New Color(1,1,1)
+		Text.Draw(canvas,Self.name,545,370)
+		Local lowy:Float=370.0
+		Local hiy:Float=371.0
+		Local lowx:Float=400.0
+		Local hix:Float=401.0
+		Local t_1:Int=ballOwnerType
+		
+		If ballOwnerType = 2
+			canvas.Color = New Color(0,0,0)
+			Text.Draw(canvas,"SOLIDS",lowx,hiy)
+			canvas.Color = New Color(1,1,1)
+			Text.Draw(canvas,"SOLIDS",hix,lowy)
+		ElseIf ballOwnerType = 1
+			canvas.Color = New Color(0,0,0)
+			Text.Draw(canvas,"STRIPES",lowx,hiy)
+			canvas.Color = New Color(1,1,1)
+			Text.Draw(canvas,"STRIPES",hix,lowy)
+		Else
+			canvas.Color = New Color(0.0,0.0,0.0)
+			Text.Draw(canvas,"Game Is Open",lowx,hiy)
+			canvas.Color = New Color(1,1,1)
+			Text.Draw(canvas,"Game Is Open",hix,lowy)
+		End
+	End
+	
+	Method ResetBalls()
+		Self.ballList.Clear()
+	End
+	
+	Method CompletedAll:Bool()
+		Return Self.ballList.Count()=15
+	End
+	
+End Class
+
+Class Player Extends User
+	
+	Field oldTouch:Int=0
+	Field oldMouseDown:Int=0
+	Field oldMouseX:Int=0
+	Field oldMouseY:Int=0
+	Field rotatorImg:Image
+	
+	Method New(name:String,cue:Ball,engine:ElasticEngine,t_ghostImg:Image[],ballImg:Image,stickImg:Image[],rotatorImg:Image,buttonImg:Image[],stripImg:Image,frameTopImg:Image[],frameBotImg:Image[],topImg:Image,botImg:Image,lrImg:Image[],meterImg:Image,stretchImg:Image,sliderImg:Image)
+		Self.cue=cue
+		Self.position=cue.P
+		Self.ballImg=ballImg
+		Self.ghostImg=t_ghostImg
+		Self.cueStick=New CueStick(cue.P,cue.radius+1.0,stickImg,180.0)
+		Self.name=name
+		User.firstShot=1
+		Self.completedSet=0
+		Self.engine=engine
+		Self.ghostBall=New GhostBall(cue.P,cue.radius,t_ghostImg,180.0,stripImg)
+		Self.control=New Control(cue,5,420,.5,meterImg,stretchImg,sliderImg,40)
+		Self.oldTouch=0
+		Self.button=New Button2(buttonImg,360,400,"shoot",Null)
+		Self.rotatorImg = rotatorImg
+	End
+
+	Method Init() Override
+		ghostBall.dx=Cos(cueStick.angle)
+		ghostBall.dy=Sin(cueStick.angle)
+		cueStick.SetAngle(cueStick.angle)
+		engine.CollisionDistance2Ghost(ghostBall)
+	End
+	
+	Method Positioning:Bool() Override
+		button.Update()
+		If button.GetState()
+			Local overlapping:Int=0
+			For Local b:Ball = Eachin engine.ballList
+				If b=cue Continue
+				Local vx:Float=Self.cue.P.x-b.P.x
+				Local vy:Float=Self.cue.P.y-b.P.y
+				If(vx*vx+vy*vy<=4.0*b.radius*b.radius)
+					overlapping=1
+				End
+			End
+			If overlapping=0 Return False
+		End
+		If Mouse.ButtonDown(MouseButton.Left)
+			If oldMouseDown
+				engine.CollisionDistance2Ghost(Self.ghostBall)
+				cue.P.x+=Mouse.X-(oldMouseX)
+				cue.P.y+=Mouse.Y-(oldMouseY)
+				If cue.P.x<476.0 cue.P.x=476.0
+				If cue.P.x>596.0 cue.P.x=596.0
+				If cue.P.y<47.0  cue.P.y=47.0
+				If cue.P.y>315.0 cue.P.y=315.0
+			End
+		End
+		oldMouseDown=Mouse.ButtonDown(MouseButton.Left)
+		oldMouseX=Mouse.X
+		oldMouseY=Mouse.Y
+		Return True
+	End
+	
+	Method SetButtonText(str:String) Override
+		button.SetText(str)
+	End
+	
+	Method Aiming:Bool() Override
+		If control.Update()
+			angle=control.angle
+			cueStick.SetAngle(angle)
+			If control.Length<0.0
+				cueStick.SetDistance(0.0)
+			Else
+				If control.Length>40.0
+					cueStick.SetDistance(40.0)
+				Else
+					cueStick.SetDistance(control.Length)
+				End
+			End
+			ghostBall.dx=Cos(angle)
+			ghostBall.dy=Sin((angle))
+			engine.CollisionDistance2Ghost(ghostBall)
+			oldAngle=angle
+		Else
+			If(control.Length>0.0)
+				cueStick.SetAnimation2()
+				control.Length=0.0
+				Return False
+			End
+		End
+		Return True
+	End
+	
+	Method ShootingAnimating:Bool() override
+		If cueStick.Update()=0
+			cue.Init(cueStick.dx*40.0*control.volume,cueStick.dy*40.0*control.volume)
+			media.PlayShootCol()
+			Return False
+		End
+		Return True
+	End
+	
+	Method RenderButton(canvas:Canvas) Override
+		button.Render(canvas)
+	End
+	
+	Method Render(canvas:Canvas,status:Int) Override
+		control.Render(canvas)
+		canvas.DrawImage(rotatorImg,position.x,position.y,-angle-1.6,1.0,1.0)
+'		If status Render(canvas,1)
+		ghostBall.Render(canvas)
+		cueStick.Render(canvas)
+	End
+End
+
+Class CueStick
+	Field position:PVector2D
+	Field len:Float=.0
+	Field image:Image[]
+	Field rotatorImg:Image
+	Field offset:Float=.0
+	Field angle:Float=.0
+	Field dx:Float=.0
+	Field dy:Float=.0
+	Field offsetx:Float=.0
+	Field offsety:Float=.0
+	Field distance:Float=.0
+	Field animStep:Float=.0
+	Field animx:Float=.0
+	Field animy:Float=.0
+	Field animAngle:Float=.0
+	Field animDelay:Int=0
+	Field power:Float=.0
+	
+	Method SetAngle(ang:Float)
+		If ang<>angle
+			angle=ang
+			dx=Cos(angle)
+			dy=Sin(angle)
+			offsetx=-dx*len
+			offsety=-dy*len
+		End
+	End
+	
+	Method New(pos:PVector2D,offset:Float,image:Image[],angle:Float)
+		Self.position=pos
+		Self.len=offset
+		Self.image=image
+		Self.offset=offset
+		Self.SetAngle(angle)
+		Self.rotatorImg = rotatorImg
+	End
+
+	Method SetDistance(d:Float)
+		distance=d
+		animStep=30.0
+		animx=-dx*distance
+		animy=-dy*distance
+	End
+
+	Method SetAnimation2()
+		animAngle=0
+		animDelay=Millisecs()+1
+	End
+
+	Method Update:Int()
+		If animDelay<Millisecs()
+			If animAngle >= 90.0
+				animx=0.0
+				animy=0.0
+				distance=0.0
+				animAngle=0.0
+				Return 0
+			Else
+				Local Length:Float=Cos(animAngle*ATR)* distance
+				animx=-dx*Length
+				animy=-dy*Length
+				If animAngle < 90.0 animAngle += animStep
+			End
+			animDelay=Millisecs()+1
+		End
+		Return 1
+	End
+
+	Method Render(canvas:Canvas)
+		canvas.Color = New Color(1,1,1)
+		Local x:Float=position.x+offsetx+animx
+		Local y:Float=position.y+offsety+animy
+		canvas.DrawImage(image[0],x,y,-angle,1.0,1.0)
+	End
+
+	Method SetAngleCpu(ang:Float)
+		If ang<>Self.angle
+			angle=ang
+			dx=Cos(angle)
+			dy=Sin(angle)
+			offsetx=-dx*len
+			offsety=-dy*len
+		End
+	End
+
+	Method Init(d:Float)
+		animStep=d/4.0
+		animx=0.0
+		animy=0.0
+	End
+	
+	Method UpdateCpu:Int()
+		If(Self.animDelay<Millisecs())
+			If(animAngle>=90.0)
+				animx=0.0
+				animy=0.0
+				animAngle=0.0
+				Return 0
+			Else
+				Local Length:Float=-Sin(animAngle*ATR)*30.0
+				animx=Length*dx
+				animy=Length*dy
+				If animAngle>90.0
+					Local pwr:Float = power/4.0
+					If pwr<1.0 pwr=1.0
+					animAngle += animStep*pwr
+				Else
+					animAngle += animStep
+				End
+			End
+			animDelay=Millisecs()+1
+		End
+		Return 1
+	End
+End
+
+Class Control
+	Field x:Int
+	Field y:Int
+	Field width:Float
+	Field height:Float
+	Field meterImg:Image
+	Field stretchImg:Image
+	Field sliderImg:Image
+	Field volume:Float
+	Field sliderx:Int
+	Field framed:Int
+	Field active:Int
+	Field oldMouseDown:Int
+	Field maxValue:Float
+	Field stp:Float
+	Field cue:Ball
+	
+	Field topLRImg:Image[]
+	Field botLRImg:Image[]
+	Field midTopImg:Image
+	Field midBotImg:Image
+	Field midLRImg:Image[]
+	Field tw:Float
+	Field th:Float
+	
+	Field x2:Float
+	Field vx:Float
+	Field y2:Float
+	Field vy:Float
+	Field dx:Float
+	Field dy:Float
+	Field Length:Float
+	Field activeVolume:Float
+	Field angle:Float
+	
+	Method New(cue:Ball,x:Int,y:Int,volume:Float,meterImg:Image,stretchImg:Image,sliderImg:Image,maxValue:Int)
+		Self.x=x
+		Self.y=y
+		Self.width= meterImg.Width
+		Self.height= meterImg.Height
+		Self.meterImg=meterImg
+		Self.stretchImg=stretchImg
+		Self.sliderImg=sliderImg
+		Self.stretchImg=stretchImg
+		If volume>1.0 volume=1.0
+		If volume<0.0 volume=0.0 
+		Self.volume=volume
+		Self.sliderx= volume*Self.width
+		Self.framed = 0
+		Self.active = 0
+		Self.oldMouseDown = 0
+		Self.maxValue=(maxValue)
+		Self.stp=Self.width/maxValue
+		Self.cue=cue
+	End
+
+	Method SetFrame(tlrImg:Image[],blrImg:Image[],mtImg:Image,mbImg:Image,mlrImg:Image[])
+		Self.topLRImg=tlrImg
+		Self.botLRImg=blrImg
+		Self.midTopImg=mtImg
+		Self.midBotImg=mbImg
+		Self.midLRImg=mlrImg
+		Self.tw= tlrImg[0].Width
+		Self.th= tlrImg[0].Height
+		Self.framed=1
+	End
+	
+	Method Update:Int()
+		Local state:Int=1
+		If Mouse.ButtonDown(MouseButton.Left)
+			If Self.oldMouseDown
+				Self.vx=Mouse.X-Self.x2
+				Self.vy=Mouse.Y-Self.y2
+				Self.Length=Self.vx*-Self.dx+Self.vy*-Self.dy
+				If(Self.Length>Self.maxValue)
+					Self.volume=Self.maxValue*Self.stp/Self.width
+				Else
+					If(Self.Length<0.0)
+						Self.volume=0.0
+					Else
+						Self.volume=Self.Length*Self.stp/Self.width
+					End
+				End
+			Else
+				Self.x2=Mouse.X
+				Self.y2=Mouse.Y
+			End
+		Else
+			If Self.oldMouseDown
+				Self.vx=Mouse.X-Self.x2
+				Self.vy=Mouse.Y-Self.y2
+				Self.Length=Self.vx*-Self.dx+Self.vy*-Self.dy
+				If(Self.Length>Self.maxValue And Self.Length>=0.0)
+					Self.volume=Self.maxValue*Self.stp/Self.width
+				Else
+					If(Self.Length<0.0)
+						Self.volume=0.0
+					Else
+						Self.volume=Self.Length*Self.stp/Self.width
+					End
+				End
+				Self.oldMouseDown=Mouse.ButtonDown(MouseButton.Left)
+				Return 0
+			Else
+				Self.volume=Self.activeVolume
+				Self.vx=Mouse.X-Self.cue.P.x
+				Self.vy=Mouse.Y-Self.cue.P.y
+				Self.angle=(ATan2(Self.vy,Self.vx))
+				Self.dx=Cos((Self.angle))
+				Self.dy=Sin((Self.angle))
+			End
+		End
+		Self.oldMouseDown=Mouse.ButtonDown(MouseButton.Left)
+		Self.sliderx=((Self.volume*Self.width))
+		Return 1
+	End
+	
+	Method Render(canvas:Canvas)
+		If framed
+			Local px:Float=x-tw
+			Local py:Float=y-th
+			canvas.DrawImage(topLRImg[0],px,py)
+			canvas.DrawImage(midTopImg,px+tw,py,0.0,width,1.0)
+			canvas.DrawImage(topLRImg[1],px+width+tw,py)
+			canvas.DrawImage(midLRImg[0],px,py+th,0.0,1.0,height)
+			canvas.DrawImage(midLRImg[1],px+tw+width,py+th,0.0,1.0,height)
+			canvas.DrawImage(botLRImg[0],px,py+th+height)
+			canvas.DrawImage(midBotImg,px+tw,py+th+height,0.0,width,1.0)
+			canvas.DrawImage(botLRImg[1],px+tw+width,py+th+height)
+		End
+		canvas.DrawImage(meterImg,x,y,0)
+		Local w:Float=(1.0-volume)*width
+		canvas.DrawImage(stretchImg,x+sliderx,y,0.0,w,1.0)
+		canvas.DrawImage(sliderImg,x+sliderx,y,0)
+		Text.Draw(canvas,"POWER",x+50,y+10)
+	End
+End
+
+Class Cpu Extends User
+	Field pocketList:List<ArcWall>
+	Field activeBallList:List<Ball>
+	Field aimWait:Int=0
+	Field delay:Int=0
+	Field inacuracy:Float=.0
+	Field power:Float=.0
+
+	Method New(name:String,cue:Ball,engine:ElasticEngine,arcList:List<ArcWall>,stickImg:Image[],ballImg:Image,inacuracy:Float)
+		Self.cue=cue
+		Self.position=cue.P
+		Self.cueStick= New CueStick(cue.P,cue.radius+1.0,stickImg,180.0)
+		Self.ballImg=ballImg
+		Self.pocketList=arcList
+		Self.activeBallList=engine.ballList
+		Self.aimWait=100
+		Self.delay=Millisecs()+Self.aimWait
+		Self.inacuracy=inacuracy
+		Self.name=name
+		Self.engine=engine
+	End
+
+	Method Init() Override
+		
+	End
+	
+	Method Positioning:Bool() Override
+		Repeat
+			cue.P.x=Rnd(476,596)
+			cue.P.y=Rnd( 47,315)
+			Local overlapped:Int=0
+			For Local b:Ball = Eachin engine.ballList
+				If b=cue
+					Continue
+				End
+				Local vx:Float=b.P.x-Self.cue.P.x
+				Local vy:Float=b.P.y-Self.cue.P.y
+				If (vx*vx+vy*vy<=4.0*b.radius*b.radius)
+					overlapped=1
+					Exit
+				End
+			End
+			
+			If overlapped=0 Exit
+		Forever
+		cue.Init(0.0,0.0)
+		Return False
+	End
+	
+	Method Get8Ball:Ball()
+		For Local b:Ball = Eachin activeBallList
+			If(b.num=8)
+				Return b
+			End
+		End
+		Return Null
+	End
+	
+	Method PathToPocket:Bool(b1:Ball,dx:Float,dy:Float)
+		For Local b2:Ball = Eachin Self.activeBallList
+			If(b2=b1 Or b2=Self.cue)
+				Continue
+			End
+			Local vx:Float=b2.P.x-b1.P.x
+			Local vy:Float=b2.P.y-b1.P.y
+			Local dp:Float=vx*dy-vy*dx
+			If(Abs(dp)<b1.radius+b2.radius)
+				Local dp2:Float=vx*dx+vy*dy
+				If(dp2>0.0)
+					Return False
+				End
+			End
+		End
+		Return True
+	End
+	
+	Method PathToBall:Bool(b1:Ball,dx:Float,dy:Float,skipBall:Ball)
+		Local vx:Float=skipBall.P.x-b1.P.x
+		Local vy:Float=skipBall.P.y-b1.P.y
+		Local len:Float=vx*dx+vy*dy
+		For Local b3:Ball = Eachin activeBallList
+			If(b3=b1 Or b3=skipBall) Continue
+			Local vx2:Float=b3.P.x-b1.P.x
+			Local vy2:Float=b3.P.y-b1.P.y
+			Local dp:Float=vx2*dy-vy2*dx
+			If(Abs(dp)<b1.radius+b3.radius)
+				Local dp2:Float=vx2*dx+vy2*dy
+				If(dp2>0.0 And dp2<Abs(len)) Return False
+			End
+		End
+		Return True
+	End
+
+	Method FindBestPocket:ArcWall(ball:Ball)
+		Local distance:Float=INVALID_DISTANCE
+		Local pocket:ArcWall
+		For Local arc:ArcWall = Eachin pocketList
+			Local dx1:Float
+			Local dy1:Float
+			Local vx1:Float=arc.target.x-ball.P.x
+			Local vy1:Float=arc.target.y-ball.P.y
+			Local vx2:Float=cue.P.x-ball.P.x
+			Local vy2:Float=cue.P.y-ball.P.y
+			Local dp:Float=vx1*vx2+vy1*vy2
+			If(dp<0.0)
+				Local d1:Float=Sqrt(vx1*vx1+vy1*vy1)
+				If(d1>0.0)
+					dx1=vx1/d1
+					dy1=vy1/d1
+				End
+				If(d1=0.0 Or Self.PathToPocket(ball,dx1,dy1))
+					Local px:Float=ball.P.x-dx1*(arc.radius+ball.radius)
+					Local py:Float=ball.P.y-dy1*(arc.radius+ball.radius)
+					vx1=px-cue.P.x
+					vy1=py-cue.P.y
+					d1=Sqrt(vx1*vx1+vy1*vy1)
+					Local ang:Float=(ATan2(vy1,vx1))
+					If(PathToBall(cue,Cos((ang)),Sin((ang)),ball))
+						If(d1<distance)
+							distance=d1
+							angle=ang
+							pocket=arc
+						End
+					End
+				End
+			End
+		End
+		Return pocket
+	End
+
+	Method GetRndSolidBall:Ball()
+		Local counter:Int=0
+		For Local b:Ball = Eachin activeBallList
+			If(b.num<8)
+				counter+=1
+			Else
+				Continue
+			End
+		End
+		If counter
+			If counter>1 counter=Rnd(1.0,(counter+1))
+			Local n:Int=1
+			For Local b2:Ball = Eachin activeBallList
+				If b2.num < 8
+					If(n=counter) Return b2
+					n+=1
+				Endif
+			End
+		End
+		Return Null
+	End
+
+	Method GetRndStripedBall:Ball()
+		Local counter:Int=0
+		Local low:Int=0
+		For Local b:Ball = Eachin activeBallList
+			If(b.num>=8 And b.num<16)
+				counter+=1
+			Else
+				low+=1
+			End
+		End
+		counter=(((low)+Rnd(1.0,(counter+1))))
+		Local n:Int=1
+		For Local b2:Ball = Eachin activeBallList
+			If n=counter Return b2
+			n+=1
+		End
+		Return Null
+	End
+
+	Method GetRndBall:Ball()
+		Local counter:Int=Self.activeBallList.Count()
+		counter=((Rnd(0.0,(counter-1))))
+		Local n:Int=0
+		For Local b:Ball = Eachin activeBallList
+			If n=counter Return b
+			n+=1
+		End
+		Return Null
+	End
+	
+	Method Aiming:Bool() Override
+		Local vx:Float
+		Local vy:Float
+		If ballList.Count() = 7
+			Local b:Ball=Get8Ball()
+			Local pocket:ArcWall=FindBestPocket(b)
+			If pocket
+				vx=b.P.x-pocket.target.x
+				vy=b.P.y-pocket.target.y
+				Local len:Float=Sqrt(vx*vx+vy*vy)
+				Local px:Float=b.P.x+vx/len*(b.radius+cue.radius)
+				Local py:Float=b.P.y+vy/len*(b.radius+cue.radius)
+				vx=px-cue.P.x
+				vy=py-cue.P.y
+			Else
+				vx=b.P.x-cue.P.x
+				vy=b.P.y-cue.P.y
+			End
+		Else
+			Local ball:Ball
+			Local pocket2:ArcWall
+			Local distance:Float=INVALID_DISTANCE
+			For Local b2:Ball = Eachin activeBallList
+				If(ballOwnerType=2 And b2.num>7) Continue
+				If(ballOwnerType=1 And b2.num<9) Continue
+				If(b2.num=8 Or b2=cue) Continue
+				
+				Local pkt:ArcWall=FindBestPocket(b2)
+				If pkt
+					vx=pkt.target.x-b2.P.x
+					vy=pkt.target.y-b2.P.y
+					Local ln:Float=Sqrt(vx*vx+vy*vy)
+					If(ln<distance)
+						distance=ln
+						pocket2=pkt
+						ball=b2
+					End
+				End
+			End
+			
+			If distance < INVALID_DISTANCE
+				vx=ball.P.x-pocket2.target.x
+				vy=ball.P.y-pocket2.target.y
+				angle= ATan2(vy,vx)
+				Local px2:Float = ball.P.x+Cos(angle)*(cue.radius+ball.radius)
+				Local py2:Float = ball.P.y+Sin(angle)*(cue.radius+ball.radius)
+				vx=px2-cue.P.x
+				vy=py2-cue.P.y
+			Else
+				Local t:Int=Self.ballOwnerType
+				If t = 2
+					ball = GetRndSolidBall()
+				Elseif t = 1
+					ball=GetRndStripedBall()
+				Else
+					ball=GetRndBall()
+				End
+				If ball
+					vx=ball.P.x-cue.P.x 
+					vy=ball.P.y-cue.P.y
+				Else
+					vx = Cos(cueStick.angle)
+					vy = Sin(cueStick.angle)
+				Endif
+			End
+		End
+		angle=ATan2(vy,vx)
+		cueStick.SetAngleCpu(angle)
+		power=Rnd(10.0,50.0)
+		cueStick.Init(power)
+		Return False
+	End
+	
+	Method SetButtonText(str:String) Override
+	End
+	
+	Method ShootingAnimating:Bool() Override
+		If Self.cueStick.UpdateCpu()=0
+			cue.Init(Self.cueStick.dx*power,cueStick.dy*power)
+			Return False
+		End
+		Return True
+	End
+	
+	Method RenderButton(canvas:Canvas) Override
+	End
+	
+	Method Render(canvas:Canvas,status:Int) Override
+		If status Super.Render(canvas,1)
+		cueStick.Render(canvas)
+	End
+End