Ping (Think Pong)

Miscellaneous Forums/Blitz Showcase/Ping (Think Pong)

I started this ages ago and just now dug it up and threw in scoring. It was for a little challenge a while ago to make a game with only ovals, lines, and text.
I did it finally!

Basically, it's Pong in a circle. The paddles looked a bit better on my old and dark screen, but they'll probably look fine on most people's screens anyway.

There are up to 5 players in the game. The first two are controlled using W,A,S,D and the arrow keys.
Scoring is simple: Hit the ball out of the ring without hitting the rectangle in the center. (I came up with that while coding, because I realized that the other method I devised would be absurd).
I've noticed that it's kind of interesting trying to play both players at once... That may make an interesting gameplay element eventually.

Um... It's not finished, but it will be a while until I dig in and and add networking for the other 3 players, powerups, and/or AI.

;Ping. (Pong? Hah! PING!!)
;By Dylan McCall

;Name explanation: Ping is based off of Pong, but rather than build a Pong clone I did something quite crazy. 
;I realized that Pong is probably named after Ping-Pong, and so rather than have the name Pong again, 
;why not borrow the other word? Thus, it is called Ping.

AppTitle "Ping. By Dylan McCall"

SeedRnd(MilliSecs())

Type Person

End Type

Type Player
	Field name$
	Field Number
	Field Angle[1]
	Field Hit
	Field Score
	Field Lane[1]
	
	Field Paddles
End Type

Global playercount = Input("Input Player Count: (1-6) ")

;Global playercount = 2

Dim pl.player(playercount - 1)
Dim plMarkers.cline(playercount-1,1)
angleInc = 360/(playercount*2)
Angle = 0
;I should have commented...

For n = 0 To playercount - 1
	pl.player(n) = New player
	pl(n)\Number=n
	b = n + 1
	pl(n)\name$= "Player " + b
	
	pl(n)\Paddles=2
	
	For pa = 0 To pl(n)\Paddles-1
		plMarkers(n,pa) = LineNew(0,0,0,0)		
		plMarkers(n,pa)\Owner = pl.player(n)
	
	
		Select b	
			Case 1			
				plMarkers(n,pa)\r = 150 : plMarkers(n,pa)\g = 150 : plMarkers(n,pa)\b = 255	
			
			Case 2			
				plMarkers(n,pa)\r = 255 : plMarkers(n,pa)\g = 150 : plMarkers(n,pa)\b = 150 
			
			Case 3			
				plMarkers(n,pa)\r = 150 : plMarkers(n,pa)\g = 255 : plMarkers(n,pa)\b = 150 
			
			Case 4			
				plMarkers(n,pa)\r = 255 : plMarkers(n,pa)\g = 255 : plMarkers(n,pa)\b = 150 
			
			Case 5			
				plMarkers(n,pa)\r = 150 : plMarkers(n,pa)\g = 255 : plMarkers(n,pa)\b = 255
			
			Case 6			
				plMarkers(n,pa)\r = 255 : plMarkers(n,pa)\g = 255 : plMarkers(n,pa)\b = 255 	
		End Select
	
		pl(n)\angle[pa] = angle
		Angle = Angle + angleInc	
	Next	

Next

Global CRectX,CRectY,CRectW,CRectH

;Draw the rectangle in the center
centerRect(325,250,150,100)

Const RingX = 5
Const RingY = 5

Const RingW = 790
Const RingH = 590

Global ballCount = 1

For t = 1 To ballCount
	BallStart()		
Next

DefFont = LoadFont("Arial",10)

Graphics 800,600,False,2

SetBuffer BackBuffer()

; Create the timer to track speed 
frameTimer=CreateTimer(100) 

;main loop
While Not KeyDown(1)

	WaitTimer(frameTimer) ; Pause until the timer reaches 60 

	UpdateHumanMovement()
	
	drawWorld()
	UpdateText()
	
	Flip
	
	Cls

Wend
End

Function UpdateHumanMovement()
	If KeyDown(205)
		If Not PaddleCollides(0,0,1)
			pl(0)\angle[0] = pl(0)\angle[0] + 1
			If pl(0)\angle[0] > 360 Then pl(0)\angle[0] = 0
			If pl(0)\angle[0] < 0 Then pl(0)\angle[0] = 359
		EndIf
	EndIf	
	If KeyDown(203)
		If Not PaddleCollides(0,0,-1)
			pl(0)\angle[0] = pl(0)\angle[0] - 1		
			If pl(0)\angle[0] > 360 Then pl(0)\angle[0] = 0
			If pl(0)\angle[0] < 0 Then pl(0)\angle[0] = 359
		EndIf
	EndIf
	If KeyHit(200)
		If Not PaddleCollides(0,0,10,True)
			If pl(0)\lane[0] < 30
				pl(0)\lane[0] = pl(0)\lane[0] + 10
			EndIf		
		EndIf
	EndIf
	If KeyHit(208)
		If Not PaddleCollides(0,0,-10,True)
			If pl(0)\lane[0] > 0
				pl(0)\lane[0] = pl(0)\lane[0] - 10
			EndIf
		EndIf
	EndIf

	If KeyDown(30)
		If Not PaddleCollides(0,1,1)
			pl(0)\angle[1] = pl(0)\angle[1] + 1
			If pl(0)\angle[1] > 360 Then pl(0)\angle[1] = 0
			If pl(0)\angle[1] < 0 Then pl(0)\angle[1] = 359
		EndIf
	EndIf	
	If KeyDown(32)
		If Not PaddleCollides(0,1,-1)
			pl(0)\angle[1] = pl(0)\angle[1] - 1
			If pl(0)\angle[1] > 360 Then pl(0)\angle[1] = 0
			If pl(0)\angle[1] < 0 Then pl(0)\angle[1] = 359
		EndIf
	EndIf
	If KeyHit(17)
		If Not PaddleCollides(0,1,10,True)
			If pl(0)\lane[1] < 30
				pl(0)\lane[1] = pl(0)\lane[1] + 10
			EndIf
		EndIf
	EndIf
	If KeyHit(31)
		If Not PaddleCollides(0,1,-10,True)
			If pl(0)\lane[1] > 0
				pl(0)\lane[1] = pl(0)\lane[1] - 10
			EndIf
		EndIf
	EndIf
	
End Function
Function PaddleCollides(p,pa,dir,LaneChanging=False)
	Local DirC,DirB
	If LaneChanging Then DirC = dir Else DirC = 0
	If LaneChanging Then DirB = 0 Else DirB = dir
	For n = 0 To PlayerCount-1
		If Not n = p
			For pad = 0 To pl(n)\Paddles-1				
				If pl(n)\lane[pad] => pl(p)\lane[pa]+dirC And pl(n)\lane[pad] =< pl(p)\lane[pa]+dirC					
					If pl(n)\angle[pad]=> pl(p)\angle[pa]-10+DirB And pl(n)\angle[pad] =< pl(p)\angle[pa]+10+DirB Then Return True
				EndIf
			Next
		EndIf
	Next
	Return False
End Function



Global ScoringPlayer
Global MiddleTxtTimer

Function CenterRect(x,y,w,h)
	l.cline = linenew(x,y,x+w,y);Top line
	l\b = 255
	l.cline = linenew(x+w,y,x+w,y+h);Right line
	l\b = 255
	l.cline = linenew(x,y+h,x+w,y+h);Bottom line
	l\b = 255
	l.cline = linenew(x,y+h,x,y);Left line
	l\b = 255
	
	CRectX=x:CRectY=y:CRectW=w:CRectH=h
End Function

Function UpdateText()
	If MilliSecs() < MiddleTxtTimer + 3000
		Color 200,200,200
	ElseIf MilliSecs() < MiddleTxtTimer + 4000
		colourChange=(MilliSecs() - MiddleTxtTimer - 3000)/5 ;I doubt that this fader works as it should
		Color 200-colourChange,200-colourChange,200-colourChange
	Else
		Return
	EndIf
	
	n=ScoringPlayer
	
	Local Points$
	If Abs(pl(n)\Score) = 1 Then Points$ = " Point." Else Points$ = " Points."
			
	;If pl(n)\Score > 0
		Text 330,270,pl(n)\Name$
		Text 330,300,"Has "+pl(n)\Score+Points$
	;ElseIf pl(n)\Score < 0
	;	Text 300,300,pl(n)\Name$
	;	Text 330,300,"Has "+pl(n)\Score+Points$
	;EndIf
	
	;n=ScoringPlayer
		
	;Text 330,260,pl(n)\Name$+" Scores"
	;If pl(n)\Score > 1 Then Text 330,300,"Has "+pl(n)\Score+" Points." ElseIf pl(n)\Score = 1 Then Text 330,300,"Has "+pl(n)\Score+" Point."
End Function
Function Score(plN)
	pl(plN)\Score = pl(plN)\Score+1
	ScoringPlayer = plN
	MiddleTxtTimer = MilliSecs()
End Function
Function NegScore(plN)
	pl(plN)\Score = pl(plN)\Score-1
	ScoringPlayer = plN
	MiddleTxtTimer = MilliSecs()
End Function

Function BallStart()
	.retry

	BallX = Rand(325,475)
	BallY = Rand(125,275)
	
	If RectsOverlap(BallX,BallY,2,2,CRectX,CRectY,CRectW,CRectH) Then Goto retry
	
	BallAngle# = Rnd(0,259)
	
	ball.Circle = CircleNew(BallX,BallY,2,Sin(ballangle) * 2,Cos(ballangle) * 2)
End Function

Function drawWorld()

	;Draw the ring
	Color 0,0,255
	Oval RingX,RingY,RingW,RingH,False
	
	;draw the ball(s)

	circleupdate()
	circledraw()
		
	;draw paddles for each player
	For p.player = Each player
		For pa = 0 To pl(n)\Paddles-1	
			RX = ringW/2 - 5
			RY = ringH/2 - 5
		
			a = p\angle[pa] - 5
		
			xA = Sin(a) * (RX-p\Lane[pa])
			yA = Cos(a) * (RY-p\Lane[pa])
			
			a = p\angle[pa] + 5
			
			xB = Sin(a) * (RX-p\Lane[pa])
			yB = Cos(a) * (RY-p\Lane[pa])
			
			linerecalc(plMarkers(n,pa),xA + 400,yA + 300,xB + 400,yB + 300)				
		Next
		n = n + 1
	Next
		
	linedraw()	

End Function

Type cline

	Field x1#,y1#,x2#,y2#,nx#,ny#,ux#,uy#
	Field r,g,b
	
	Field Owner.player

End Type

Function LineNew.cline(x1#,y1#,x2#,y2#)

	l.cline=New cline
	
	LineRecalc(l,x1#,y1#,x2#,y2#)
	
	Return l

End Function

Function LineRecalc(l.cline,x1#,y1#,x2#,y2#)

	l\x1=x1
	l\y1=y1
	l\x2=x2
	l\y2=y2

	dx#=l\x2-l\x1
	dy#=l\y2-l\y1
	
	d#=Sqr(dx*dx+dy*dy)
	If d#<0.0001
		d#=0.0001
	EndIf
	
	l\ux=dx/d
	l\uy=dy/d
	
	l\nx#=l\uy
	l\ny#=-l\ux

End Function

Function LineDraw()

For l.cline=Each cline
	
	Color l\r,l\g,l\b
	Line l\x1,l\y1,l\x2,l\y2
	
;	Color 255,255,0
;	;Draw normal
;	xm#=(l\x1+l\x2)/2.0
;	ym#=(l\y1+l\y2)/2.0
;	Line xm,ym,xm+l\nx*10,ym+l\ny*10

;	n = n + 1
	
Next

End Function

;Global LineCollisionX#
;Global LineCollisionY#

;Returns the shortest distance from a point to a line
;Use LineCollisionX and LineCollisionY to get the collision point.
Function LineDistance#(l.cline,x#,y#)

	dx#=l\x2-l\x1
	dy#=l\y2-l\y1
	
	d#=Sqr(dx*dx+dy*dy)
	
	px#=l\x1-x#
	py#=l\y1-y#
	
	dist#=(dx*py-px*dy) / d
	
	LineCollisionX=x#-l\nx*dist#
	LineCollisionY=y#-l\ny*dist#
		
	Return Abs(dist#)
	
End Function

;Returns true if a point collides with a line within range r
Function LineCollide(l.cline,x#,y#,r#)
	
	dx1#=x-(l\x1-l\ux*r)
	dy1#=y-(l\y1-l\uy*r)
	
	d#=Sqr(dx1*dx1+dy1*dy1)
	
	dx1=dx1/d
	dy1=dy1/d
	
	dx2#=x-(l\x2+l\ux*r)
	dy2#=y-(l\y2+l\uy*r)
	
	d#=Sqr(dx2*dx2+dy2*dy2)
	
	dx2=dx2/d
	dy2=dy2/d
	
	dot1#=dx1*l\ux+dy1*l\uy
	dot2#=dx2*l\ux+dy2*l\uy
	
	Return ((dot1#>=0 And dot2#<=0) Or (dot1#<=0 And dot2#>=0)) And (LineDistance(l,x,y)<=r)
	
End Function

Type Circle

	Field x#,y#,vx#,vy#
	
	Field vel#
	
	field veladd#

	Field r#
	
	Field LastHit.cline,LastLastHit.cline

End Type

Function CircleNew.Circle(x#,y#,r#=50,vx#=0,vy#=0)
	
	c.Circle=New Circle
	c\x=x
	c\y=y
	c\r=r
	c\vx=vx
	c\vy=vy
	
	c\veladd# = 1
	
	;CirclePlace(c)
	
	Return c
End Function

;draw circles
Function CircleDraw()

Color 255,255,255
For c.Circle=Each Circle
	
	rh#=c\r*2

	Oval c\x-c\r,c\y-c\r,rh,rh

Next


End Function

Function CircleUpdate()

For c.Circle=Each Circle

	;Calculate total velocity
	c\vel#=Sqr(c\vx*c\vx+c\vy*c\vy#) ;+ c\veladd
	
	;collision against other circles
	For cc.Circle=Each Circle
		;do not test against itself
		If cc<>c
			;vector from one circle to another
			dx#=cc\x-c\x
			dy#=cc\y-c\y
			d#=Sqr(dx*dx+dy*dy)
			;check of distance is smaller than the two circle´s radii together
			If d<(c\r+cc\r)
		
				;make the vector a unit vector (length=1), multiply it with the circle´s
				;total velocity, to get the new motion vector
				c\vx=(-dx#/d) * c\vel
				c\vy=(-dy#/d) * c\vel
			
			EndIf
		EndIf
	Next
	
	;collision against lines
	For l.cline=Each cline
	
		for veladd# = 1 to c\veladd# step 0.1
		
			x=c\x+c\vx * veladd#
			y=c\y+c\vy * c\veladd#

			;Check if circle collides with a line
			If LineCollide(l,x,y,c\r)
			
				if c\veladd# < 2 then c\veladd# = c\veladd# + 0.1 ;boost speed of the ball with each bounce
				
				;debuglog "Velocity: " + c\vel			
				debuglog "Added to speed: " + c\veladd
					
				;Get the dot product between the circles motion vector and the line´s normal vector
				dot#=c\vx*l\nx+c\vy*l\ny	
				
				;Calculate the circle´s new motion vector
				c\vx=c\vx-2.0*l\nx*dot
				c\vy=c\vy-2.0*l\ny*dot
				
				;Make note of the last collision
				If Not c\lasthit = Null
					If c\LastHit\Owner = Null
						If Not c\LastLastHit = Null							
							If Not c\LastLastHit\Owner=l\Owner
								;Score(l.cline\Owner\Number)
								NegScore(c\LastLastHit\Owner\Number)
							EndIf
						EndIf			
					EndIf
				EndIf		
				c\LastLastHit = c\LastHit.cline	
				c\LastHit = l.cline					
			
				exit
						
			EndIf
			
		next

	Next
		
	;add velocity to position
	c\x=c\x+c\vx * c\veladd#
	c\y=c\y+c\vy * c\veladd#		
		
	;Wrap to screen boundaries
	If c\x>GraphicsWidth()
		c\x=0
	EndIf
	
	If c\y>GraphicsHeight()
		c\y=0
	EndIf

	If c\x<0
		c\x=GraphicsWidth()
	EndIf	
	
	If c\y<0
		c\y=GraphicsHeight()
	EndIf
	
	If c\y = 0 Or c\y = GraphicsHeight() Or c\x = 0 Or c\x = GraphicsWidth()
		If Not c\LastHit = Null
			If Not c\LastHit\Owner = Null
				Score(c\LastHit\Owner\Number)
			EndIf
		EndIf
		Delete c
		If ballCount = 1
			BallStart()
		Else
			ballCount = ballCount - 1
		EndIf
	EndIf
Next

End Function


Yes, I know the source code is a mess and the only clean part (the circle and line bouncing functions) is not mine.

mIRC quote of the day: Ping? Pong!

Reminds me of the circular Pong game LineOf7s did for one of the "No external files" competition I ran years ago.

His ball always bounced off a circle in the middle tho.

Hehe, it's almost identical to a small game I did in Director ages ago. Have a look here: G-Pong

Just did some further fiddling to make it rather odd :)
Edited top post.

If a shot is bounced off the middle and an opponent catches it, the person who last hit it loses a point. Also, paddles collide with eachother (which will be interesting once players have two paddles each), and to avoid being blocked in, they can also change lanes.

I intend to slow it down a bit to allow for the absurd gameplay that I have arranged to actually work.


Other fiddling. Players now have 2 paddles each... Still up to 6 players because there was room in colour choices.